GAミント至上主義

Web Monomaniacal Developer.

このブログについて(先頭固定)

【お約束】 投稿内容は個人の見解であり、所属する(していた)組織の公式見解ではありません。

名前:uyamazak(昔いた会社で上司が開発用Linuxサーバーのユーザー名に「yuyamazaki」が長いので勝手に作ってくれた。読み方わからないけどウヤマザク)

高校あたりからWEBサイトをやったり趣味の延長でWEB系を仕事にした感じの人間。

2020年1月から株式会社シニアジョブにジョイン。
2021/1現在、まだ開発者は3人、これからの会社なのでビジネス貢献しつつ、きれいな設計をしたい

アズールレーン@竹敷でモバイルのUI、UX研究中(初嫁ジャベリン)
フレンド&大艦隊メンバー募集してます ID:939524678 @竹敷



GitHub: https://github.com/uyamazak/

これまでの主なプロジェクト

続きを読む

FirebaseにNuxtをデプロイするときにmaxInstancesを指定する

こちらの記事で高額請求怖くなったのでメモ。

Next.jsをFirebaseにデプロイしたら高額請求がきて貯金がなくなりかけた話 #googlecloud - Qiita

自社用管理画面側でNuxtのアプリをFirebaseにデプロイしている。

Nuxtのバージョンはこれ。

"nuxt": "^3.8.0",

Cloud ConsoleでCloud Functionの設定で手動で変えてみたが、デプロイするとまたデフォルトの100に戻ってしまうのでデプロイ時の設定が必須だった。

変更前

Nuxtの公式ドキュメントのFirebaseを見ると、親切にもmaxInstancesの設定例があるのに気づいた。

そのため参考記事のようにfirebase.configではなくnuxt.configを利用する。

Deploy Nuxt to Firebase

nuxt.config.ts

export default defineNuxtConfig({
// 省略
  nitro: {
    firebase: {
      gen: 2,
      httpsOptions: { minInstances:0, maxInstances: 3 },
      nodeVersion: "20",
    },
  },
})

管理画面で常にアクセスされるわけではなく、費用節約を重視し、minは0で指定。

これでデプロイしたら無事反映された

変更後

映画『ゆるキャン△』の作業服に近い2023年現行製品3選

焚き火などがしたくなってきたので、そろそろ綿100%で安心して焚き火ができる作業服を買おうと思ったところ、 映画『ゆるキャン△』で作業服を着ていたのを思い出した(記事書いてる時点でもまだAmazon Primeで観れた)。

検索したら公式グッズとして出てきた。

www.famitsu.com

しかし、普通の作業服だったら上下セットで高くて1万円ぐらいのところ、3万円超えで高すぎる上に、ちょっと太めでだぼだぼしてる。

2年近く毎月のように作業着を買い、作業着通販サイトを眺め、メーカーのカタログを見てきた知識を活かして選んでみる。

選定のポイント

見た目はなるべく近いもの

色やポケットなどはなるべく合わせる。シルエットもだぼっとしたものではなく細めのものとしたい。

襟は全員隠されてて謎。

ファスナー下部の留めボタンが露出しているが、だいたい商品などに傷をつけないよう隠されているものが多いので難しそう。

綿100%

キャンプで火を扱うため、火の粉などで穴が空きやすい化繊(ポリエステル、ナイロン等)は避け、綿100%は必須。

激しい動きはしないし、たまにしか着ないのにポリウレタン入りは劣化が避けられないため、ストレッチ性は妥協する。  

厚手の秋冬もの

作業着には一般的に薄手の春夏と、厚手の秋冬モデルがあるが、作品に合わせ秋冬の厚地ものにする。

以上の条件にあうものを探した。

実売価格の参考には通販サイトのものをリンク。

BURTLE 8031シリーズ アースグリーン

画像はカタログから

WEB CATALOG - BURTLE WORKWEAR NEW ARRIVAL

広島県府中市のメーカーバートルの綿100%ラインナップのうち、一番条件に近いやつがこれ。かっこよさそう。

製品洗いによる色具合も良い。生地は日本製チノクロス

実売は上8031、下8032セットで7千円程度。 バートル 8031 ジャケット 3,760円|作業服・作業着ならまいど屋

ただしMサイズからで女性サイズ向けサイズがない。 厚手の8131シリーズには女性向けS,SSがあるが、色がなかった。

SOWA 5042シリーズ グレー

※10/14追記 

岡山県倉敷市のメーカーSOWA(桑和)の2020年頃に発売した比較的新しい商品。

通販サイトでは気づかず、メーカーカタログで存在に気づいた。公式ドキュメント大事。

ボタン、ポケットなどの配置も近く、色の名称はグレーながらもカタログ画像を見る感じ緑っぽさもあり、イメージに近い。シルエットもそんなにダボついてない。

しかも、日本製生地でクラボウ オフルージュによる防汚に加え、綿100%なのにストレッチというなかなか見たことないハイスペック。

更には女性向けサイズ(SS、S)がラインナップされているので完璧っぷり。モデル写真にも女性。

画像はカタログから

ワーキングウェア(2023-24秋・冬)|株式会社桑和 SOWA

参考価格として上5042-00が5,423円、5042-08が4,664円と上下でセット1万円超えで今回の中では一番高い。

桑和 5042-00 長袖ブルゾン 4,930円|作業服・作業着ならまいど屋

通販サイトの画像などだと普通にグレーで緑っぽさがなさそうなので色合いがちょっと問題か。

SOWA 5333シリーズ ミストブルー

SOWA(桑和)の定番(主力)商品。

防シワ、縮防止機能付き。

ちょっとシルエットは太めかも。Mからなので女性サイズはなさそう。

画像はカタログから

ワーキングウェア(2023-24秋・冬)|株式会社桑和 SOWA

実売価格で上5333と下5338で9,000円弱。

桑和 5333 長袖ブルゾン 4,180円|作業服・作業着ならまいど屋

番外:自重堂 80600 アースグリーン ※カーゴパンツなし

10/13追記 上は太めながらも条件に近いが、下に同シリーズのカーゴパンツないので番外としつつも一応書いておく。

画像はカタログから

2023AW_seifukuhyakka | ebook5

実売は上80600で4,000円程度。 自重堂 80600 ジャンパー 3,710円|作業服・作業着ならまいど屋

全然なかった・・・

もっとたくさんあると思ってたので3つ選ぼうと思ったのに2つで終わった(10/14 SOWA 5042追加で3つに) 。

バートル、桑和の他にも自重堂、寅壱コーコス信岡あたりは目を通したつもり。

特に自重堂は自治体作業着のシェアが強いイメージがあり、作品内容的にあると思ってたけど条件に合うものがなかった。

薄緑の色で綿100%は想像以上に少ない。静電気防止がついた電気系向け、室内作業向けに薄緑は多いのかもしれない。

しかもSOWA 5042を除いてはMサイズからしかないので作中の女性たちが着ていたかもというリアリティがないのが残念。

また両胸が同じポケットというのも少なく、片方ファスナー付きが多数。

ということで、この3つどれかに「ふじかわ松ぼっくりキャンプ場」、もしくは「週末戦士作業着レンジャー」を名入れして買えばいいんじゃないかな。

他にもあったら教えてください

ElasticsearchのFunction score queryでBoolean値や日付をソートに使う

シニアジョブの求人一覧で、全体の件数も増えてきて、募集を終了した求人も参考として表示する必要が出てきた。

seniorjob.jp

他求人サイトでも参考として出してるとこも多く、メルカリで販売済みも出てくるのが似てる。

ただ表示するだけでなく募集中の求人は優先的に先に表示したい。

単純なsortでなんとかしたかったけど、公開終了日を過ぎてるかどうかの判定では、リクエスト時点の計算が必要なので、function_score、script_scoreが必要になった。

サービスで使っているバージョンは7系だけど8のドキュメントで特に問題なかった。

Function score query | Elasticsearch Guide [8.10] | Elastic

ElasticsearchもPainlessもJavaもまだまだわからないことだらけなのでメモ。

リクエストquery

ひとまず動いたElasticsearchへのqueryはこんな感じになった。 Node.jsでやってるのでJSONではなくJavaScriptのオブジェクトなので注意。適当にいじってるので階層とかずれてるかも。

 query: {
  function_score: {
    query: // 他のいろんな条件
    script_score: {
      script: {
        lang: 'painless',
        source: `
          def createdTimeStamp = doc['createdDate'].value.toInstant().getEpochSecond();
          def publicValue = doc['status'].value == 'public' ? 1 : 0;
          def endSeconds = doc['endDate'].value.toInstant().getEpochSecond();
          def notEndValue = params.nowSeconds < endSeconds ? 1 : 0;
          return createdTimeStamp + (publicValue * 2000000000) + (notEndValue * 1000000000)
        `,
        params: {
          nowSeconds: new Date().getTime() / 1000,
        },
      },
    },
  },
},

Score計算

基準のタイムスタンプ

基本的には公開日順なので公開日createdDateのタイムスタンプをベースにした。大きい=新しい方がscoreが高くなる。

並び順にミリ秒制度は必要ない+なるべく値を小さくするため秒単位を使う。

例として2023/09/29 11:11:18だったら 1695953478

def createdTimeStamp = doc['createdDate'].value.toInstant().getEpochSecond();

公開終了判定

endDateフィールド名には、終了日が入っている。終了日がない求人もあるが、nullはやっかいなので100年後の日付を入れてる。

公開終了日に近いほうが上に来たほうがいいかな?とも思ったけど、上記公開日順もあるので今回は0,1のbool値にした。

単純な比較なので3項演算子

def notEndValue = params.nowSeconds < endSeconds ? 1 : 0;

この1 or 0を上記公開日より確実に優先させるため2000000000倍して足す。最初の1桁でわかりやすくなる。

終了日前だけのときはこの値 最初の1桁が3

1695953478
+
2000000000
=
3695953478

現在のタイムスタンプに関しては、最初はPainlessで書いたけど、レコードごとに計算するのも無駄だし Elasticsearch側の計算を少しでも減らすためJS側で計算して、paramsで渡す形式にした。1秒ずれることもあるけど問題なし。

params: {
  nowSeconds: Math.trunc(new Date().getTime() / 1000),
}

ステータス判定

statusフィールドに公開中だったら'public'が入っており、そのときscoreを上げる。 public以外のステータスは考慮不要なのでこちらもbool値。

最初statusをなぜかtextフィールドにしてしまっていたので、エラーになっていた。keywordでないと比較ができないっぽい?

def publicValue = doc['status'].value == 'public' ? 1 : 0;

publicだけのときはこの値で最初の1桁が2

1695953478
+
1000000000
=
2695953478

Score最終形

statusがpublicかつ、終了日過ぎてないときは↓になって最初の1桁が4となり最強になる。

1695953478
+
1000000000
+
2000000000
=
4695953478

逆にどちらのstatusがpublic以外かつ終了日も過ぎてる場合は加算がなく1695953478のままなので最初の1桁は1のまま。

こんくらいの条件だったらこれで十分そうだけど、もうちょっと複雑なったら考えなおさないとだめそう。

Painless言語、Reason: runtime errorとの戦い

記述にはPainless言語を使ったけど、だめなときのログにはReason: runtime errorしか出ないのはpainfulだった。 ベースのJavaもわからないので単純ミスでハマりまくる。

ERROR errors: search_phase_execution_exception: [script_exception] Reason: runtime error

www.elastic.co

エラー詳細の出し方ありそうだけど、よくわからんのでscript_valueで一つずつ値を確認して、script_scoreに移植していく形を取った。

     // デバッグ中にscript_fieldsを使った、公開時は不要なので消す
      script_fields: {
        debug_value: {
          script: {
            lang: 'painless',
            // 見たい値だけ出してた
            source: `
            def endSeconds = doc['endDate'].value.toInstant().getEpochSecond();
            return endSeconds
            `,
            params: {
              nowSeconds: Math.trunc(new Date().getTime() / 1000),
            }
          },

        },
      },
    }

あとはリクエスト時にsortを特に指定しなければ、このscore順になった。