めあとるーむ日記帳

なんか書く

MySQLの全部検索の設定で注意すべきところ

MySQLには標準で全文検索機能がある。分かち書きが必要とは言えワイルドカードで検索するよりよっぽどましなので使った方がいいケースも多い。

ただしこの全文検索機能は「検索文字の文字数が4文字以下はヒットしない」という条件がある。まあ高速化のためにはある程度考えるべきなのだろうが、英語ならともかく日本語では4文字以下の単語の比率が結構高いと思うし英語でもcatとかdogとか簡単な単語が引っかからないため、そのあたりは変更しなければならない。

で、ft_min_word_lenの値を1にして再起動、インデックスを張りなおしていざ検索したところ、なぜかヒットしない。

うんうん悩んで設定を見ていくと似たような設定にinnodb_ft_min_token_sizeというのもあった。これはエンジンでInnoDBをつかう場合の検索条件らしく、外部キーの設定などでInnoDBを使っていたのでこれかと思いこちらの設定値を0にしたところうまくいった。

多くのfulltext関係の資料ではft_min_word_lenの変更で設定が変わると書いていたので勘違いした。ただ、もしかするとこちらの数値も変更する必要があるのかもしれない。とはいえいちいち変更して試すのがめんどいので申し訳ないが誰かやってまとめておいてほしい

GolangのAppEngineをデプロイしようとしたら失敗した。どうやらファイル名に日本語は使わない方がいいらしい

golang で AppEngine 開発してさあデプロイするぞ!とやったところエラーが出た。

PS > gcloud app deploy
Services to deploy:

(プロジェクト情報略)

Do you want to continue (Y/n)?  Y

Beginning deployment of service [news]...
Some files were skipped. Pass `--verbosity=info` to see which ones.
You may also view the gcloud log file, found at
(出力されるログファイルの場所情報略)
#============================================================#
#= Uploading 0 files to Google Cloud Storage                =#
#============================================================#
File upload done.
ERROR: gcloud crashed (UnicodeDecodeError): 'utf8' codec can't decode byte 0x82 in position 4: invalid start byte

If you would like to report this issue, please run the following command:
  gcloud feedback

To check gcloud for common problems, please run the following command:
  gcloud info --run-diagnostics

今までできてて突然できなくなったので変更した場所が悪いのかな?と思うもエラー内容(UnicodeDecodeError)はPythonのエラーらしく、どうやらgcloudコマンド関係がクラッシュした模様(gcloudコマンドはPythonで実装されてる)

とはいえアップデートなどもなかったはずなのでやはりこちらが何か変更したのが悪いはずと思いいろいろと試したところ、どうやらプロジェクトファイル内のドキュメント関係を入れたディレクトリに日本語名のファイルがあることに気づく。

以前のデプロイでは存在していなかったファイルの名前を日本語から適当な英語のファイル名にしたところ無事デプロイ成功

どうやら関係ないファイルでも日本語名のファイルがあると問題が起こるらしい。確かに文字コード関係のエラーだもんね。

なお環境がWindowsなので要はこれShift-JISで名前が入ってるらしくその問題もあるのかもしれない。今後気を付けよう

CircleCIでMavenを使ってGoogle AppEngineをデプロイするのを自動化したい

追記(2017-09-08)

解決してないのでググってたどり着いた人ごめん


タイトルの通り。

GAEのデプロイは2通りあって、appcfgを使うかMavenを使うか

Deploying a Java App  |  App Engine standard environment for Java  |  Google Cloud Platform

おススメ(recommended)なのはMavenらしいので、それを使っていたのだが、せっかくだし自動化したいなーと思ってCircle CIでデプロイコマンドに mvn appengine:update をそのまま入れたところ、まあ当然なのだが認証が通らず止まってしまった。デフォルトではOAuthで認証してキーを取ってきてそれを入れると通るのだが、当然入力する場所はないのでどうしようもない。


というところで止まっている。お盆が明けたら再開予定……

可能ならサービスアカウントを登録させるか引数で渡すべきなのだと思う……

ちなみにほかのデプロイ方法ではどうやらgcloudコマンド用の設定がCircle CIに用意されているらしい。


追記(2017-09-08)

ようやく追記(タイトルもちょっと修正)

とりあえず結論は出来なかった。できなかったというよりわからなかったが近い。

サービスアカウントの情報を渡して認証させてもどうやら maven appengine コマンドが読んでくれない。

認証関係のオプションを探してみたがそれらしいものはなかった。一応auth関係のオプションも探したがそれらしいものも見つけられなかった。

pythonコマンドを用いたデプロイやらコンテナベースのサービス?なら手段もそれなりにあるっぽいが、残念ながらわからないというのが実情。むなしい

値を返してから代入する

ちょっと前にTwitterで人が死ぬバグといって流れてきたツイートがそれなりに話題になっていたと思う。

これね。出典元は 非推奨だった bool 型に対するインクリメント演算子を削除 - cpprefjp C++日本語リファレンス

これは古いCのコードをもとにしていて、かつ当時はbool型もなかったのでこうなっていたようだが、配列の要素を並べて表示し、それぞれの要素間に,を表示するといったパターンではおそらく昔はよくあったコードだと思う。


この「配列の要素を並べて、各要素間にはコンマをうって表示する」というケースに類似した処理はよくあるものだと思う。

地味に面倒なことに、この場合「最初は飛ばすがそのあとからは毎回つける」というパターンで記述することが多い。

で、その場合(下のコードはJava

boolean first = true;
for(Object obj : objList){
    if(!first){
        System.out.print(",");
    }else{
        first = false;
    }
    System.out.print(obj.toString());
}

みたいなコードが必要になったりする。

はっきり言って面倒である。

ちなみにC++では上のリンクであるように昔はインクリメントがあった。bool型(boolean型)のインクリメントはJavaにはないし、C++も今はない。

後述するがインクリメントははっきり言ってわかりにくいものなので最近だとSwiftでは方にかかわらずインクリメント自体がなくなった。

ではこのインクリメントなしで書くにはどうするか、というと、まあ上のように一つフラグを用意してあげてとなるのだが、C++では割と便利な関数ができていた。

それがstd::exchangeというので、std::exchange - cppreference.comにある。

これは「値を返してそのあと代入する」というものである。

ただこれはJavaにはない。Javaにそんな便利なものがあると思ったら大間違いだ。欲しければ作れ。という感じでこんな感じになるだろうか

public class Exchanger<T>{
    T value;
    public Exchanger(T value){
        this.value = value;
    }

    public T get(T newValue){
        T oldValue = value;
        value = newValue;
        return oldValue;
    }
}

使うときはこう

Exchanger<Boolean> first = new Exchanger<Boolean>(true);
for(Object obj : objList){
    if(!first.get(false)){
          System.out.print(",");
    }
    System.out.print(obj.toString());
}

ちょっとはキレイになった。

問題点はJavaなんでプリミティブ型が使えないこととか、Integer型のときに +1 で回せないという点あたりか。後者の問題はそのままの値を返すget()メソッドで値を取って+1したものをnewValueとしてget(T newValue)に渡す感じにするだろうか。ジェネリクスを使う以上あまり入り込んだ処理は行いたくない。

いずれにせよJavaだと面倒。ほかの言語であればだいぶ楽(C#とかならLINQ使ったりできるかもしれない)なので調べる価値はあるはず。


ところでインクリメント(と、デクリメント)について。

いわゆる++だが、先述の通りはっきり言ってややこしい。i++++iの違いなど意識していては可読性が下がるしバグの温床になりかねない。

SwiftではSwift2からインクリメントとデクリメントがなくなったし、正直forループの処理以外で使うこともほとんどないだろう。可能な限り使わないに越したことはない。

C++の場合演算子オーバーロードでは前置か後置かの判定で描き方も面倒になっている。ついでに言うと += などの書き方も正直あまりいい物ではないと思う。

省略することが良いとは限らないし、現在なら基本はすべて記述すべきだと思う。

コンストラクタの引数がいいのか、setterを使った方がいいのか

クラス変数やメンバ変数を置くとき、当然その変数に何らかの値を入れるものである。

で、その変数になにか値を入れるタイミングはコンストラクタで入れるべきなのか、それとも呼び出しもとでsetterを呼び出して入れるべきなのか、どちらがいいのかわからなくなった。わからないなりになにか文章に直そうと思ってこの記事を記す。

わからないので、もしこの記事で「バカジャネーノ」とか思った方がいれば教えてほしい。教えて。

コンストラクタで渡す場合

要は

Object obj = new Object(hoge);

みたいなケースである。

これが強制されるケースはJavaであればfinal修飾子を、C#であればreadonly修飾子を付けた変数はコンストラクタで値を決めるか、変数宣言時に値を決定する必要がある。ちなみにC#のconstは宣言時に値を決定しなければならない。

逆に言えばコンストラクタで値を決定できるともいえる。

メリット

  • setterを作らないことでクラス変数やメンバ変数のアクセス制御ができる
  • 呼び出し側に記述を作らない(ケース次第)

setterで渡す場合

コードに直すと

Object obj = new Object();
obj.setHoge(hoge);

というケース。

これコンストラクタが強制されるケース以外特に制限はない。

メリット

  • setterで制限や値の調整ができる
  • 呼び出し側に記述を作る(ケース次第)
  • 外部からの値の操作ができる(あんまりよくない)

比較

各々のメリットの欄に書いたが、呼び出し側に記述を作ることが場合によっては良かったり悪かったりする。

例えばゲームプログラミングで敵キャラを生成するときにHPなどの各ステータスをいちいちsetterで渡すのは正しいとは言えない(もっともこの辺は敵の種類を渡したら一発で処理できるようにすべきだし、そうであってもコンストラクタに渡せばいけるはずである)

Enemy enemy = new Enemy(enemyType);
// もっと正しくするにはcreateEnemy(enemyType)とかFactoryパターンやBuilderパターンを使うべきだと思う

こういったケースならコンストラクタに渡すべきだろう。

また、逆に言えば呼び出し側が呼び出すべき内容であればsetterでおくべきだということである。

外部からの操作に関していえば、setterであっても制限もできる。

void setHoge(Object hoge){
    if(hoge!=null){
        this.hoge = hoge;
    }
}

とすれば、nullでない場合だけ入るという形にできる。要は値が設定されていない場合だけ入るという形で、あとから外部で値を変更するというのは通じないことになる(どこかでnullになるような処理でない限り)。

総括

もっと言えば、極論はコードの書き方次第だしある程度の思想が統一された状態で書けばどちらでもいいのだと思う。

思うのだが、いかんせんなんかいい感じの資料とかもないのでいまいちどうやればいいのかわからないという感じはする。

Java Servlet ではPOSTでステータスを受け取れるContentTypeはapplication/x-www-form-urlencodedだけ?

ServletRequestインターフェースのgetParameter()メソッドでパラメータを取り出すとき、GETはできるのにPOSTにした瞬間うまくいかないことがあった。

理由を調べたところ、getParameter()あるいはそれに類する処理をPOSTメソッドのRequestからの場合、content-type=application/x-www-form-unlencoded でなければ読み込めないらしい。

ちなみにgetInputStream()とかを用いて生のデータを読めば別にこのメソッドを使わなくても読めるらしい。つまりBody部に入っているが、パース出来ないか読めないか読まないかそんなところのようだ。 ただしgetInputStream()とかとgetParameter()を併用するとデータがおかしくなることもあるとか。

もともと(というか本来?)POSTはフォームの入力のデータを扱う場合に使うことが多いからその影響かもしれない。もしServletでWebAPIみたいなことしたい、POSTでデータ送信したいってケースで引っかかることがあるかもしれない。

application/json や text/plain なども通らないってのはちょっとつらい。JSONはまあはやってる割に素で対応できる環境が少ないからまあいいけど、うーん。

まあapplication/x-www-form-urlencodedに偽装して送り付けるのが一番楽かな


参考資料

HttpServletRequest (Servlet 2.4 API 仕様)

厳密にはServletRequestインターフェースに実装されてるのでこっちだけど

ServletRequest (Servlet 2.4 API 仕様)

これのgetParameter()に書いてる

ファイル名が違ってgitの状態がおかしくなる

原因もなぜそうなったのかもよくわからない問題だけど一応記す。

git + github で readme ファイルを修正してコミットしてプッシュしてマージした。したが、どうもおかしく変更が適用されていないっぽい。

コミット忘れかと思ったがコミットしているし、readme ファイルそのものは変更されていてリポジトリのトップに表示される readme だけ変更されていなかった。

あららおかしいぞと思いつつ VSCode の git を見るとコミット対象に README がある。

> git status をしてみたところコミットされていないしインデックスに登録もされていないとのこと。

おかしいぞと思いつつ > git diff をしたところ修正前のものと修正後のものが出た。ただ、修正自体はコミットしたはずである。

実際、>git add -A>git commit -am "commit" とかやってもコミット対象はないよ、加えるファイルもないよと出る。

VSCodeの機能でコミットしようとすると今度はエラーが発生したと出た。

よく見たところ追加対象の “README” と実際にファイルとして存在する “readme” は違うファイルのようで、どういうわけか二つの名前のファイルが1つの存在として扱われているため起きていたみたいだった。詳しいことはわからん。でもファイル名が違っていた。

とりあえず対処法としては

> git reset --hard HEAD

で戻して

> git status

で確認。この時点で変更はないと出る。たぶん修正前に戻っている。

そしたら

> git mv README readme

で名前変更。あとは

> git add -A
> git commit -am "commit"

でコミットして

> git status

あたりで確認して、問題なければプッシュ。とりあえずこれでどうにかなった。

原因はわからないが、ファイル名に気を付けるといいと思われる。