やってみる

アウトプットすべく己を導くためのブログ。その試行錯誤すらたれ流す。

Rust自習(人称辞典 3)

 LIKE句で絞り込んだレコードをランダム出力する。

成果物

LIKE句

 LIKE句でカンマを含む値にマッチさせたい。SQL文なら以下のような。

sqlite> select * from FirstPersons where (',' || comment || ',') LIKE '%,私的表現,%';

 以下パターンにマッチする。

  • *私的表現*
  • *,私的表現*
  • *私的表現,*
  • *,私的表現,*

コード

 これをdieselで書くと以下。

main.rs

    let results = personal_pronoun::schema::FirstPersons::dsl::FirstPersons
        .filter(id.eq(rnd_id as i32)
            .and(comment.eq("私的表現").or(like("私的表現,%")).or(like("%,私的表現")).or(like("%,私的表現,%")))
        )

 エラー。

error[E0425]: cannot find function `like` in this scope
  --> src/main.rs:25:40
   |
25 |             .and(comment.eq("私的表現").or(like("私的表現,%")).or(like("%,私的表現")).or(like("%,私的表現,%")))
   |                                            ^^^^ not found in this scope

 調べてみた。

 なんと、Nullable<>だとlikeが使えない……。

SQLのNULLは罠

 そもそも、SQLのNULLは諸々の弊害を生み出すようだ。よって全フィールドにNOT NULL制約したほうがいい。そしてオプション項目ならdefault ''のようにDEFAULT制約する。

DB再設計

マイグレーション生成

diesel migration generate recreate_not_null_FirstPersons

FirstPersonsテーブル生成

up.sql

-- 既存のテーブルをリネーム
alter table FirstPersons rename to tmp_FirstPersons;
-- 新しいテーブルを作成(元々のテーブル名と同じ名前で)
create table FirstPersons(
    id integer not null primary key,
    value text not null,  -- 代表的な表記
    ruby text not null default '',   -- ふりがな、ルビ
    comment text not null default '' -- 補足
);
-- レコードを全て移す
insert into FirstPersons(id, value, ruby, comment) select id, value, ruby, comment from tmp_FirstPersons;
-- 元のテーブルを削除
drop table tmp_FirstPersons;

 down.sqlはいいや。

マイグレーション実行

diesel migration run

ロールバック方法

diesel migration redo

 確認。変更されている。OK。

$ sqlite3 ./PersonalPronoun.sqlite3
...
sqlite> select * from sqlite_master;
...
table|FirstPersons|FirstPersons|7|CREATE TABLE FirstPersons(
    id integer not null primary key,
    value text not null,  -- 代表的な表記
    ruby text not null default '',   -- ふりがな、ルビ
    comment text not null default '' -- 補足
)

マイグレーションを最初からやり直したいとき

  1. PersonalPronoun.sqlite3ファイルを削除する
  2. migrations/.gitkeepファイルを削除する
  3. diesel migration runコマンドを実行する

 一気に最新版になる。

schema.rs

table! {
    FirstPersons (id) {
        id -> Integer,
        value -> Text,
        ruby -> Text,
        comment -> Text,
    }
}

models.

#[derive(Queryable)]
pub struct FirstPersons {
    pub id: i32,
    pub value: String,
    pub ruby: Option<String>,
    pub comment: Option<String>,
}

 NullableはなくなったのでOptionを外して以下のようにする。

#[derive(Queryable)]
pub struct FirstPersons {
    pub id: i32,
    pub value: String,
    pub ruby: String,
    pub comment: String,
}

main.rs

extern crate diesel;
extern crate personal_pronoun;

use self::diesel::prelude::*;
use self::personal_pronoun::models::*;
use self::personal_pronoun::*;
use rand::{distributions::{Distribution, Standard},Rng,seq::SliceRandom};

fn main() {
    use personal_pronoun::schema::FirstPersons::dsl::*;
    let connection = establish_connection();

    let count: i64 = personal_pronoun::schema::FirstPersons::dsl::FirstPersons.count().get_result(&connection).unwrap();
    println!("count: {}", count);
    let mut results = personal_pronoun::schema::FirstPersons::dsl::FirstPersons
        .filter(comment.eq("私的表現")
            .or(comment.like("私的表現,%"))
            .or(comment.like("%,私的表現"))
            .or(comment.like("%,私的表現,%")))
        .load::<self::personal_pronoun::models::FirstPersons>(&connection)
        .expect("Error loading table.");

    results.shuffle(&mut rand::thread_rng());
    println!("{}: {}", results[0].id, results[0].value);
}

 ランダム方法を変えねばならない。方法は2つ。

  • SQL条件に一致した全レコードを取得し、その中からランダムで取得する(results.shuffle(&mut rand::thread_rng());参考1,参考2
  • SQLite3のrandom()関数を使う

 SQL固有のやり方は嫌なのでrustのランダムを使った。

実行

 成功!

$ cargo run
...
count: 123
7: 儂

対象環境

$ uname -a
Linux raspberrypi 4.19.42-v7+ #1219 SMP Tue May 14 21:20:58 BST 2019 armv7l GNU/Linux

前回まで