データベースインデックスの作成に関する感想

あけましておめでとうございます。陳です。
今日は、データベースのインデックスについてお話ししたいと思います。ご存じの通り、SQLインデックスはクエリの検索パフォーマンスを向上させ、データベースの効率を高めるためのものです。


インデックス作成

例:CREATE INDEX idx_name ON Student (name);

複合カラムインデックスもよく使われています。


例:CREATE INDEX idx_name_class ON Student (name, class);


インデックス削除

例:DROP INDEX idx_name ON Student; Indexを作成したら、EXPLAIN でindexの使用状況を確認できます。
実行したら、以下のような結果が得られます。
EXPLAIN SELECT * FROM Student WHERE name = 'John';

+----+-------------+---------+------------+------+---------------+----------+-----+------+------+----------+----------------+
| id | select_type | table | partitions | type | possible_keys | key   | key_len | ref | rows | filtered | Extra |
+----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+----------------+
| 1 | SIMPLE | Student | NULL | ref | idx_name | idx_name | 767 | const| 1 | 100.00 | Using index|
+----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+----------------+


説明:

- id: クエリの選択部分の識別子。外部のクエリの場合は1です。
- select_type: SELECTのタイプ。この場合はSIMPLEで、サブクエリやUNIONのない単純なSELECT。
- table: アクセスされているテーブル。
- type: 使用されているアクセス方法のタイプ(例:フルテーブルスキャンの場合はALL)。
- possible_keys: 使用可能なインデックス。
- key: オプティマイザによって実際に選択されたインデックス。
- key_len: 使用されたキーの長さ。
- ref: インデックス名で指定された列または定数。
- rows: クエリを実行するためにMySQLが調査する行の推定数。
- filtered: テーブル条件によってフィルタリングされた行の割合。
- Extra: クエリの実行に関する追加情報。
 
初めてのindexなら、私はまずpossible_keysにこのindexが考えられるかどうかを確認します。次に、実際に選択されたindexとクエリを実行する情報を確認します。
 
最も重要なのは rows 列です。なぜなら、これはMySQLがクエリに回答するために処理する必要がある行の数を示しています。extra の情報もクエリの実行方法をより理解しやすくします。例えば、extra に ‘using index condition; using where;‘ と書かれている場合、インデックスを使用した後に結果をフィルタリングする必要があったことを示します。
 
複数の複合カラムインデックスを作成して、運用する時、考え通りに動かないことがありますね。explainを実行すると、possible_keysにはあるのに、使われていませんといったケースが発生することがあります。また、使われていても、クエリの効率が上がらないこともあります。
 
それは、複合カラムインデックスに設定されたカラムの順序のためかもしれません。
 
この順序と実行されたクエリ条件の順番が異なる場合、使われてもらっていない状況があります。そのため、複合カラムインデックスを作成する際は、順序が重要です。頻繁にクエリの条件として使用される列が左側にあるほど良いです。
 
また、列の一意性が高いほど、より多くの値をフィルタリングでき、検索の速度が向上します。列の分散性も考慮する必要があります。分散性は、各値の数量の差異を示します。
 
迷った時、Index Hints というものを使ってクエリにするINDEXを指定して違うIndexを使用してexplainで結果を考えることも便利と思います。
 
USE INDEX はindexを使用することを提案しますが、必ずしも指定通りには使用されません。FORCE INDEX は指定したINDEXのみを使用します。
例:
SELECT *
FROM Student USE INDEX (idx_age)
WHERE age > 20;
 
今回の感想は以上です。EXPLAIN に興味がある方は、以下の記事での説明が詳しく、理解しやすいので、お勧めさせていただきます。

Using EXPLAIN in MySQL to analyze and improve query performance:
https://medium.com/datadenys/using-explain-in-mysql-to-analyze-and-improve-query-performance-f58357deb2aa

PHPのstdClassの動きが環境によって違った件

スズキです。PHPのstdClassのプロパティにアクセスしようとしたときの挙動が、環境によって異なる現象に遭遇したので、共有です。

やりたかったこと

  • json文字列の中から「orderDate」というプロパティから日付データを取り出したい
  • 「orderDate」というプロパティが存在しない場合は、代わりに今日の日付を使いたい
json文字列のイメージはこんな感じ。
{
    "orderId": 1001,
    "orderDate": "2023-11-30",
    "itemName": "UVコーティングシール"
}

修正前コード(問題あり)

PHPのjson_decodeでjson文字列からstdClassを作って、jsonの中身を使うプログラムです。
// $jsonObjectは「stdClass」クラス
$jsonObject = json_decode($jsonString, false);
try {
    $prop = $jsonObject->orderDate;  // ①orderDateがないときはExceptionが発生する…はず?
    $orderDate = new DateTime($prop);  // ②$propがnullだとTypeErrorが発生
} catch (Exception $e) {
    $orderDate = new DateTime();  // ③今日の日付を使う
}
①の行にて「orderDate」というプロパティがないとき、Exceptionが発生してくれれば、catchブロックの③にジャンプして今日の日付を$orderDateにセットできる、ということを目論んだコードなのですけど、これがダメでした。 自分の開発環境では①にてErrorExceptionが発生したので、期待通りcatchブロックへジャンプしたのですが、別のサーバに乗せたら①にてErrorExceptionは発生せず、nullが取得されたのです。nullが取得されたことで、その後の②でTypeErrorが発生したのですが、TypeErrorはExceptionを継承していないのでcatchできず、結局処理としてはエラーになってしまったというわけです。 つまり、json_decodeで取得した「stdClass」の挙動が、環境によって異なったのですね。個人的には、 $jsonObject->orderDate; のところで、プロパティが無ければnullが取れるのが直感的で好きです。

修正後コード(問題解消)

「stdClass」クラスの挙動の違いはさておき、そもそもjsonのプロパティがないときの処理を例外処理を利用して実現しようとしているのが圧倒的によくないので、普通にif文で書き直しました。なんで例外処理でやろうと思ったのだろう、当時の私…
// $jsonObjectは「stdClass」クラス
$jsonObject = json_decode($jsonString, false);
if (property_exists($jsonObject, 'orderDate') && !empty($jsonObject->orderDate)) {
    $prop = $jsonObject->orderDate;
    $orderDate = new DateTime($prop);
} else {
    $orderDate = new DateTime();
}

まとめ

ということで、「stdClass」クラスに存在しないプロパティにアロー演算子(->)でアクセスしたときの挙動は環境によって異なり、ErrorExceptionが発生したり、nullが取得できたりする、というメモでした。PHPのバージョン違いかも?と思いきや、確認したら同じバージョン番号だったので謎は深まるばかり。 どういう環境だと挙動が異なるのか、についての調査は難航しそうだったので、今回はこのへんで…

JsonSchemaについて

こんにちは、黒羽です。 ここしばらく開発しているアプリケーションでは、GUI画面の入力項目やブラウザからWebSocketのメッセージとして渡されたパラメータをJsonSchemaファイルに定義した条件でバリデーションするという実装を行ったので定義ファイルを作成するときに詰まったポイントなどを共有します。   そもそもJsonSchemaってなんやねんってときはググればだいたい解決できる世の中ですが、ものすごくざっくりというとプロパティの検証に必要な条件をJson形式で記述したスキーマ言語です。 以下のJsonはかの有名な「見た目は子供、頭脳は大人」な某小学生のプロフィールを基に書いてみました。
{
    "name": "江戸川コナン",
    "age": 6,
    "birthdayMonth": "May",
    "birthdayDay": 4,
    "sex": "male",
    "job": ["student",
        "detective"
    ]
}
この情報をベースに某小学生の通う小学校に入ることのできる人物を定義してみました。
{
    "$schema": "https://json-schema.org/draft/2019-09/schema",
    "$id": "http://example.com/example.json",
    "type": "object",
    "default": {},
    "required": [
        "name",
        "age",
        "birthdayMonth",
        "birthdayDay",
        "sex",
        "job"
    ],
    "properties": {
        "name": {
            "type": "string",
            "minLength": 1,
        },
        "age": {
            "type": "integer",
            "default": 0,
            "minValue": 5
        },
        "birthdayMonth": {
            "type": "string",
            "enum": [ "Jan", "Feb", "Mar", "Apr", "May","Jun", "Jul", "Aug"
            , "Sep", "Oct", "Nov", "Dec"
            ]
        },
        "birthdayDay": {
            "type": "integer",
            "default": 1,
            "maxValue": 31,
"minValue": 1 }, "sex": { "type": "string", "default": "", "enum": [ "male","female" ] }, "job": { "type": "array", "default": [], "items": { "type": "string", "enum": [ "student", "detective", "parent" ] } } } }
定義した条件としては
  • 名前を1文字以上持っていること
  • 5歳以上であること
  • 誕生月の略称がenumで定義した項目に含まれること
  • 誕生日が1~31の範囲であること
  • 性別が男女のどちらかであること
  • 職業が学生、探偵、保護者のいずれかであること
となります。 主題から脱線しそうなのでこれ以上の深掘りはしませんが某黒ずくめの組織の長髪の兄貴だとか警察関係者は立ち入り禁止、眠りの名探偵は職業が探偵、保護者なので立ち入り許可が出ます。 基本的なデータ型のチェック(type)、数字型の値の範囲の判定(minValue, maxValue)、リスト内の項目合致(enum)と基本的なものであれば上記の定義だけで十分です。 if分岐なども可能なので条件によって子プロパティが動的に変わるプロパティなども定義はできますがかなり記述量が増えます。 ここからは不便な点、詰まった点を共有します。

判定の結果がエラーだった場合において、エラー理由を取得すると日本語以外のメッセージしか取得できない

現状、JsonSchemaのエラーメッセージは日本語に対応しておらず、英語含めて3か国語程度しか選択できないようです。 日本国内での使用が一番想定されている開発中のアプリではユーザーに英語のエラーメッセージを解読してもらうのは忍びないのでJsonSchemaに用意されている予約語以外のフィールド名を使用するとAnnotationsというフィールドにオブジェクトとしてまとめられる仕様を利用して日本語メッセージを返すようにしています。

動的なプロパティを作ろうとすると記述量が膨大になる

if分岐があると上で書きましたがif-thenをJson形式で書くため、素直に書くと記述量がとんでもないことになります。 今回のアプリではcase文のような分岐が必要だったため以下のような雰囲気で記述しました。
"allOf": [
        {
            "if": {
                "properties": {
                    "job": {"const": "student"}
                }
            },
            "then": {
                "properties": {
                    "details": { "$ref": "#/definitions/student" }
                }
            }
        },
        {
            "if": {
                "properties": {
                    "job": { "const": "detective" }
                }
            },
            "then": {
                "properties": {
                   "details": { "$ref": "#/definitions/detective" }
                }
            }
        }
    ]
見慣れないプロパティはドキュメント等を参照してもらうとして、条件としては「jobが”student”だった場合はdefinitionsプロパティに定義したstudentのdetailsプロパティを参照し、jobが”detective”だった場合はdetectiveのdetailsプロパティを参照する、それ以外はdetailsを持たない」となっています。 allOfのおかげでここは比較的に簡潔に書けました。(それでもdefinitions項目に別途色々定義したりする必要があるのでJson自体はカオス。)

Formatのチェックがあるがかなり甘い

文字列が特定の形式にのっとっているかを判定してくれるFormatフィールドがあり、URIやIPアドレスの形式チェックができます。これは便利!!!と思って使っていますが細かいところでNGパターンがチェックからすり抜けます。止む無し。 試した過程であったのは
  • IPアドレス→第4オクテットがなくても正常と判断される
  • URI→「:/」でも正常と判断される
といったものです。他のサイトを調べても「Formatだけじゃ不十分だからPatternできちんと正規表現で潰してね!」みたいな感じだったのでFormatとPatternの併用で潰している箇所があります。(ポート番号はさすがにないけど最大値最小値で判定するのは微妙だったのでここも正規表現を使って潰していたり。)  

終わりに

今回はJsonSchemaについてでした。 入力値の簡単なチェック処理をソースコードにだらっと書くのが嫌い、美しくない!と思う方は試してみると面白いかもしれないです。

CSSカスタムプロパティ色々便利なようです

ほぼ書き終わった記事を下書きで保存したら最初と最後だけになりました。
真ん中はどこへ行ったのでしょう?

株式会社イメージ・マジックの技術ブログ前回の担当のsoenoです。

最近CSSカスタムプロパティを触ることがあり、そのうちの機能で使わなかったところが気になったので コードと合わせて紹介します。


CSSカスタムプロパティとは?

CSSカスタムプロパティ(またはCSS変数)は、CSSで再利用可能なスタイル情報を格納し、管理するための仕組みです。
つまりscss環境を作らなくてもcssで変数が使えます。
また、scssの変数と合わせて使うこともできます。


CSSカスタムプロパティの使い方

通常のCSSプロパティと異なり、カスタムプロパティは--(ハイフン2つ)で指定します。 記載場所はcssで:root{}のカッコ内に指定すると(グローバルなスコープになるので) そのcssの読み込まれていれば別のcssからでも使えるようになります。 使うときはvar();で囲います。

指定して使用する例を以下に挙げます。
:root {
  --main-bg-color: blue;
}

#main{
  background:var(--main-bg-color);
}
上の例の#main{}の中でもCSSカスタムプロパティーを指定することができますが、その場合は#main{}の外では使えません。


javascriptからCSSカスタムプロパティの値を変更する

さて、本題です。 CSSカスタムプロパティで指定した値はjavascriptから変更することができます。 以下に例を挙げます。
<!DOCTYPE html>
<html lang="ja">

<head>
    <meta charset="utf-8">
    <title>カスタムプロパティデモ</title>
    <style type="text/css">
        :root {
            --h1Color: #e70d9e;
            --h2Color: #9e0de7;
            --h3Color: #350de7;
            --h4Color: #0d4be7;
            --h5Color: #0da2e7;
            --h6Color: #0de77a;
        }

        h1 {
            color: var(--h1Color);
        }

        h2 {
            color: var(--h2Color);
        }

        h3 {
            color: var(--h3Color);
        }

        h4 {
            color: var(--h4Color);
        }

        h5 {
            color: var(--h5Color);
        }

        h6 {
            color: var(--h6Color);
        }

        #stage{
            display: flex;
        }
        #inputArea {
            margin: 30px;
            width: 350px;
            flex:1;
        }
        #inputArea label{
            display: inline-block;
            width: 225px;
        }
        #inputTargetArea {
            display: flex;
        }
    </style>
</head>

<body id="stage">
    <div id="targetArea">
        <h1>h1 hello world</h1>
        <h2>h2 hello world</h2>
        <h3>h3 hello world</h3>
        <h4>h4 hello world</h4>
        <h5>h5 hello world</h5>
        <h6>h6 hello world</h6>
    </div>
    <div id="inputArea">
        <div id="inputTargetArea">
            <label for="targetChanger" name="targetChanger">何を変更するか選べます。</label>
            <select id="targetChanger">
                <option value="h1">h1</option>
                <option value="h2">h2</option>
                <option value="h3">h3</option>
                <option value="h4">h4</option>
                <option value="h5">h5</option>
                <option value="h6">h6</option>
            </select>
        </div>

        <div id="inputColorArea">
            <label for="colorChanger">何色に変更するかを選べます。</label>
            <input type="color" id="colorChanger" name="colorChanger" value="#cbcbcb" />
        </div>
    </div>

    <script language="javascript">
        document.getElementById("colorChanger").addEventListener("input", changeColor, false);
        function changeColor() {
            var targetTagName = document.getElementById("targetChanger").value;
            var newColor = this.value;
            document.documentElement.style.setProperty('--' + targetTagName + 'Color', newColor);
        }
    </script>
</body>
</html>
例は次のようになっています。

 ・cssカスタムプロパティでh1~h6の要素に文字色を指定。  ・セレクトボックスで各要素を指定できるように。  ・カラーピッカーで指定した色をCSSのカスタムプロパティーの変更で指定の要素に反映させる。

例では一対一なので良さがわかりにくいですが、変数で指定した個所すべてが変わります。 ですので指定したcssを引き継げばページをまたいだ指定や、そのユーザーのみのページテーマといった指定もできそうです。


結論

scss等使わないとできなかった変数での管理などがCSSカスタムプロパティでできるようになってます。 今回紹介はしていませんが、cssでなくjsで値を指定するなんてこともできます。 CSSカスタムプロパティ色々便利です。

Google Spreadsheet APIを使ってみる その1

はじめに

こんにちは、イメージ・マジックのもあいです。
GoogleのSpreadsheet APIを仕事で使用したので備忘録として残しておきます。

プロジェクトの作成

Googleアカウントでログインしておいて、Google Cloud コンソール の APIライブラリページ にアクセスし、プロジェクトの選択をクリックします


新しいプロジェクトをクリックします

プロジェクト名を入力して作成をクリック

しばらくするとプロジェクトが作成されます。

APIを有効にする

プロジェクトを作成した後、プロジェクトを選択すると下記の様になるので、Drive APIとSpreadsheet APIを有効にします。

Google Drive APIを選択して、有効にするをクリックします

Google Spreadsheet APIを選択して、有効にするをクリックします


認証情報の作成

Google Drive APIとGoogle Spreadsheet APIを有効にしたら認証情報を作成をクリックします

認証情報の作成は下記の様に選択をして次へボタンをクリックします

サービスアカウントIDを入力して作成して続行ボタンをクリック。サービスアカウントが作成できましたと表示されるので、完了ボタンをクリックします。

これでサービスアカウントが作成できました。
左ペインの認証情報をクリックして、サービスアカウント欄のサービスアカウントをクリックします。

キーをクリックします

鍵を追加をクリックし、新しい鍵を作成をクリックします

JSONを選択して作成をクリックします

jsonファイルがダウンロードされました。今後このファイルと画面に表示されているサービスアカウントのメールアドレス(testsample@testsample-398107.iam.gserviceaccount.com)を保存しておいてください。Driveでのファイルオーナーになります。

終わりに

今回はプロジェクト作成から認証情報の作成までとなります。認証情報作成で取得したjsonファイルの取扱には注意してください。間違ってもGitHub等のソースコード管理にコミットしてはいけません。

サーバ上のエラー発生行をブラウザからPhpStormで開く方法

こんにちは岡野です。
今回はPHPでの開発が楽になる方法を紹介します。最初の設定は面倒ですがメリットは大きいため、PhpStorm(もしくはIntelliJのPHPプラグイン)を使っている方はぜひ試してください。
   
  • 環境
    Windows
    IntelliJ IDEA 2023.2 + PHP Plugin
    Symfony(おそらく他のフレームワークでも対応可能)
    PhpStorm Protocol (最終編集2023-08-17)
  • インストール
  1. https://github.com/aik099/PhpStormProtocol のCodeボタンからzipをダウンロードする。  
  2. zip内の「PhpStorm Protocol (Win)」 を C:\Program Files\ へコピーする。  
  3. C:\Program Files\PhpStorm Protocol (Win)\run_editor.reg をダブルクリック。OK押下。  
  4. run_editor.jsを編集。上記PhpStormProtocol説明ページに載っている方法でうまくいかない場合は、editor変数を直接変更する。  
     
    editor = ‘”C:\\Users\\ユーザ名\\AppData\\Local\\Programs\\IntelliJ IDEA Ultimate\\bin\\idea64.exe”‘;
     
  5. Symfonyの設定を行う。 例は、サーバ:/opt/app/、ローカル:c:\git\app\symfony/というマッピングの場合。
(Symfony6.1以上の場合)
.env.localに以下を記述。
SYMFONY_IDE=phpstorm://open?file=%f&line=%l&/opt/app/>c:/git/app/symfony/
 
(Symfony6.0以下の場合)
framework.yaml内のframeworkにide要素を追加。
framework:
  ide: 'phpstorm://open?file=%%f&line=%%l&/opt/app/>c:/git/app/symfony/'
 
・実行
エラー発生時にファイルパスのリンクをクリックすると、該当箇所がIntelliJで開く。

Twigでも文字列連結

初めまして。石川です。
今日は、最近業務内でした新しい発見について共有させていただきます。

Twigで文字列連結するには

大したことではないのですが今までTwigでは文字列連結ができないと思って業務に取り組んでいました。
なぜなら、Twig触り始めたばかりの時に{{str1.str2}}という風に書いてエラーが出力されたからです。
その時に、きちんと調べなかったことが原因で、conntroller側で文字列連携した変数を作成してTwig側に渡すという風に切り抜けてきました。
しかし、今回きちんと調べてみると、{{str1~str2}}という風にすることで文字列連結できることを発見しました。

Twigで参照するには

ちなみに、ドットだと文字列連結ではなく参照が可能みたいです。
{% set array = {‘x’: 11, ‘y’:12} %}
{{ array.x }}
上記記載のようにすると参照できます。

Twigで変数展開するには

変数展開は{{ “aaa #{1 + 10} bbb”}}という風に、#{}で囲むことによってできます。 上記の結果は”aaa11bbb”となります。

最後に

Twig側で処理する方が、簡潔に書ける場合も多いのではないかと思うので、状況を見てTwig側に処理を書くのも良さそうです。 以上石川から共有させていただきました。

“0”はTRUEかFALSEか

 初めまして、陳です。今日は、数ヶ月前に文字列処理を行った際のミスと感想を少しご紹介したいと思います。

 文字列の値を判定する際には、有効な文字列であるかどうかや空でないかどうかを気にします。通常、渡された値がNULLか空の文字列であるかどうかをチェックします。

 そのとき、最初に思い浮かぶのはIF条件文ですよね。

 IF条件式は、プログラミングの中で最もよく使われる便利なものだと思います。条件を設定し、結果がTRUEかFALSEかによって処理を分岐させることができます。パラメータの値を基本的にチェックする際には、ほとんどの場合、欲しい値に合致しているかどうかを判断するためにIF条件式を使用します。

 FALSEかの判定に対して、最も簡単で便利な方法は、プログラミング言語におけるデータ値のTRUEかFALSEの判定を利用することです。よく知られているJavaScriptとして、FALSEと判定されるデータ値は以下の通りです:
  • JavaScript:
    – undefined
    – null
    – ブール値の false
    – 数値のゼロ: 0
    – 浮動小数点数のゼロ: 0.0
    – NaN (Not a Number)
    – 空の文字列: “”
 その時、私はそのような認識を持ち、PHPで空の文字列の判定をif(!$string)で処理していました。そして、私は間違えました。

 JavaScriptでは、文字列に対して、””(空文字列)はFALSEと判定されることを知っています。パラメータの値が空文字列でない場合、TRUEと判定されます。したがって、”a”や”0″のいずれもTRUEとなります。

 しかし、PHPの世界では少し異なります。FALSEと判定されるデータ値は以下の通りです:
  • PHP:
    – null
    – ブール値のfalse
    – 数値のゼロ: 0
    – 浮動小数点のゼロ: 0.0
    – 空の配列: []
    – 空の文字列: “”
    – 文字列”0″
 PHPでは、””(空文字列)以外にも、”0″という文字列もFALSEと判定されます。そのため、if(!$string)という条件だけでチェックすると、判定が誤ってしまうことがあります。

 文字列が空値でないのに、FALSEと判定されることに違和感を覚えたので、原因を調べてみました:

 PHPは、ウェブリクエストでの使用を前提として設計されており、頻繁に文字列の入力(URLパラメータやブラウザのフォームからのPOSTリクエストなど)を扱います。そのため、PHPは文字列を他の型に自動的にキャストします。

 これの簡単な例として、’1′ + ‘2’は3を返し、エラーや’12’などの他の解釈は行いません。同じロジックで、文字列’0’は数値0として使用することができます。

 ’0’は数値の0として使用できると言っていますが、数値の0は「空」です。PHPでは、「ゼロである」という特性を「文字列としての特性」よりも重視するため、’0’は「偽」と見なされます。

 要するに‘0’ == 0 == false もしくは (bool)’0′ === (bool)(int)’0′

(資料参考URL

 つまり、’0’という文字列は実は数値0として扱われ、そのためにFALSEと判定されるのです。
そのため、PHPで空文字列かどうかを判定したい場合には、次のような条件式を直接記述することが最善です。
  • $string === “”と空文字列を比較する
  • empty($string) というPHPの組み込み関数を使用して空値かどうかを判定する
  • strlen( $string ) === 0 と文字列の長さが0かどうかをチェックする

 以上、今回、皆さんとシェアしたい内容でした。

ファイルアップロードを伴うWebAPIの設計パターンを比べる

こんにちは、スズキです。 ファイルアップロードを伴うWebAPIを開発する機会がぼちぼちあるのですが、ファイルアップロード周りの方式をどんな設計にするかを迷いがちなので、考えを整理してみました。

方式1:multipart/form-dataを使う

ファイルアップロードの基本、multipart/form-dataをAPIリクエストに使う方式です。
  • 単純なAPIならコマンドでも比較的ポンと気軽に送信できるのが魅力
  • JSONベースのシステムから送信するときにリクエストを別途組み立てないといけないのが面倒

こういうときに採用すると良さそう

  • テキストデータが少なくファイルのやり取りがメインのAPIをつくりたいとき
  • コマンドベースで使えるようなシンプルなAPIをつくりたいとき

方式2:JSON+Base64エンコードを使う

application/jsonをAPIリクエストに使う方式です。ファイル自体のデータもBase64エンコードしてJSONデータの中に含めてしまいます。
  • JSONベースのシステムからAPIを使いやすいのが魅力
  • ファイルのエンコード・デコードの処理時間が余分にかかるのと、Base64エンコードすることでファイルサイズが増えるので通信時間が増える

こういうときに採用すると良さそう

  • ファイルサイズが小さくテキストデータのやり取りがメインのAPIをつくりたいとき
  • リクエストもレスポンスもすべてJSONでやり取りできるAPIをつくりたいとき

方式3:multipart/form-dataとJSONを使い分ける

開発するAPI群のうち、ファイルアップロード用APIだけmultipart/form-dataを使い、その他のAPIにはapplication/jsonを使う方式です。ファイルアップロード用APIでファイルを特定するためのキーを発行し、その他のAPIではそのキーを使ってファイルを指定します。
  • JSONベースのシステムから使いやすく、アップロードしたファイルを他のAPIで何度も使いまわしやすいのが魅力
  • ファイルアップロードが個別のAPIなため、APIのリクエスト回数が増える

こういうときに採用すると良さそう

  • サイズの大きいファイルと複雑なテキストデータのどちらも取り扱うAPIをつくりたいとき
  • アップロードしたファイルを何度も使いまわせるAPIをつくりたいとき

以上、どちらかというと私の備忘録な投稿でした。

文字列の途中の不要な文字列を正規表現で検知して消してみる

こんにちは。くろはです。 今回は意外と使う場面のありそう(個人的な感想)な”文字列途中の可変な不要箇所を正規表現を用いて消す”ということについて書こうかと思います。

背景


私はDBを操作する際のGUIアプリケーションとしてHeidiSQLというアプリケーションを使用しています。
HeidiSQLは単一テーブルから抽出したレコードをInsert文の形式でエクスポートできる機能があるため、
時々この機能を利用して開発環境からマスタデータをローカル環境に移してきて開発を行っています。
ただしここでそのままInsert文を実行してしまうと主キーとなっているID列が開発環境の状態のままテーブルに格納されてしまいます。
たいていは気にしなくてもよいのですがローカル環境も元々はいつぞやの時点の本番環境をコピーしたものなので開発環境のIDをそのまま使うと
自分の行っている改修とは無関係な動作を引き起こしかねない(=正しい結果が得られない)ことにつながるパターンがあったりします。
そこでid列を指定せずにレコードを挿入するとテーブル定義上は連番でIDを振ってくれるのでinsert文からID列と値の指定箇所を消してから挿入、といった作業を時々しています。
10行くらいなら「まぁ手作業でもよいか…」となりますがさすがにそれ以上は労力が見合ってくれません。 というわけで表題の正規表現で一気に指定箇所を置換してしまおうと思った次第です。

結論

このようなSQLが複数あったとしてID列とその値を1度に消します。
INSERT INTO `テスト` (`id`, `○○`, `△△`,`□□□`) VALUES (4401241, 'hoge', 'huga', 'hogehoge');
置換前と置換後の正規表現です。 置換前 (.+)id,(.+) VALUES ([0-9]*,(.+) 置換後 $1$2 VALUES ($3 置換した後のSQLは次のように変更されます。 このようなSQLが複数あったとしてID列とその値を1度に消します。
INSERT INTO `テスト` (`id`, `○○`, `△△`,`□□□`) VALUES (4401241, 'hoge', 'huga', 'hogehoge');
置換前と置換後の正規表現です。 置換前 (.+)id,(.+) VALUES ([0-9]*,(.+) 置換後 $1$2 VALUES ($3 置換した後のSQLは次のように変更されます。  
INSERT INTO `テスト` (`○○`, `△△`,`□□□`) VALUES ('hoge', 'huga', 'hogehoge');
無事ID列と値だけが消えました。  

考え方

正規表現における記号の意味等は割愛しますが置換前のパターン構造を文字で解説すると、 【文頭からidまでの任意の文字の繰り返し】 + 【`id`,】 + 【id,からVALUESまでの任意の文字の繰り返し】+【VALUES ( 【0~9の任意の数字の繰り返し+「,」】】+ 【以降文末までの任意の文字の繰り返し】 という感じです(逆に複雑か….?) 正規表現では”()”でくくると1つのグループとして評価されます。(=文字列とは判定されない) そのため「VALUES ( 」 として「 ( 」をエスケープしてあげないと正しくパターンを判定できません。 置換後の正規表現は結構シンプルで 【1つ目のグループに含まれる文字列】+【2つ目のグループに含まれる文字列】+【VALUES (】+ 【3つ目のグループに含まれる文字列】 というパターン構造になっています。 置換前のパターンの箇所で「”()”でくくるとグループとして評価されます」と書きましたが”()”にはもう一つ、後方参照という役割があります。 後方参照とはざっくりというとカッコ内のパターンに合致する文字列を記憶し、パターン内の変数の値として該当する文字列を返してくれます。 置換後の正規表現パターンの$1~$3がその変数にあたります。 これにより変更が不要な文字列をそのまま元の文字列から持ってくることができます。 青線が今回削除した部分、赤線がかっこでくくったり文字列をそのまま指定して新しく作る文字列として指定している部分です。こうやった方が分かりやすかったかも。 今回はIDの値にあたる部分がVALUES ( の後に来る文字列だったためVALUES ( をわざわざべた書きでパターンとしたけどもっと簡潔に書けたかもしれないと思えてきました。話が脱線しそうなので今回はここまでです。

終わりに

改めて見返すと正規表現のパターンの書き方って難しいけどどうパターンを組むかというのがパズルのようで楽しかったりするんですよね。ただカッコでネストしまくるとあっという間に迷宮の出来上がりなのでできるだけ簡潔に書けるように普段から意識していきたいですね。