Humanity

Edit the world by your favorite way

Go の hashicorp/go-multierror 使ったら訳わからん挙動に遭遇してしまった

基本的な Go の挙動が理解できてないのかもしれないですが、なぜ以下のコードで (2) err != nil: true となるのかが分かりません。 誰か教えて… (g() がメソッドなのはなるべく元のコードと似せたせいです)

gist.github.com

追記

KoRoN さんとりんだんさんに教えてもらいました。

というわけで教えてもらった記事の最初に「Goのinterfaceの実体は参照と型情報のペア」と書いてありました。 Go 公式の FAQ にも書いてあります。 こっちのが私のコードより問題と解決策が一目で分かりやすかったので皆さんもハマる前にぜひ。

MongoDB で190万件あるコレクションに $regex で LIKE 検索する時にパフォーマンスの観点で気を付けること

控えめに150万件以上と言ったけど実際は190万件ぐらいだった。 以下の全てのクエリは 0 件を返す。 というか今見たら $and 使う必要はなかった。

// 89790ms
db.getCollection('colName').find({$and: [{name: /(あああ)/}, {status: 1}]})
// 1361ms
db.getCollection('colName').find({$and: [{name: /^(あああ)/}, {status: 1}]}).hint({name: 1})
// 4ms
db.getCollection('colName').find({$and: [{name: /^あああ/}, {status: 1}]}).hint({name: 1})
// 107386ms
db.getCollection('colName').find({$and: [{name: /^(あああ)/}, {status: 1}]})
// 86117ms
db.getCollection('colName').find({$and: [{name: /^あああ/}, {status: 1}]})

/(あああ)/ よりヒントなしの /^(あああ)/ が遅いのが謎。 ちなみに文字列検索した時は普通にインデックスが効くっぽいのですぐ返ってくる。

// 15ms
db.getCollection('colName').find({$and: [{name: "あああ"}, {status: 1}]})

公式ドキュメント

$regexIndex Use の項参照。

  • case insensitive なパターンを使うこと
  • ^\A で始めること
  • 先頭からのマッチの長さをなるべく短くする
    • 例えば /^a/, /^a.*/, /^a.*$/ だったら /^a/ が一番速い
    • [私見] 安易に .* とか書かずにどうしても必要なら non-greedy match にするといいかも (.*?)

あとドキュメントに書いてないけど冒頭の結果から分かるのは

  • () を使わない

もですかね。

TODO

$explain した結果も貼る。

Puppeteer で楽天銀行の入出金明細を保存

以前楽天銀行ワンタイムパスワードを突破しつつ明細履歴を TSV ファイルに保存するスクリプトを書いた。 ワンタイムパスワードのメールを受け取って本文をパースするために IMAP にログインしてメールが来るまで待つ、みたいな事をしている。

tyru.hatenablog.com

前のは id:motemen さんのスクリプトを参考にさせてもらいながら書いたので Protractor + WebDriver 製だった。

ただ最近はなんやかんや (Headless Chrome + Chrome Debugging Procotol) で ChromeGUI のない環境でも動かす事ができて、 さらにヘッドレス Chrome を操作する Puppeteer というライブラリもある。 しかもこれは Google 自身が開発しているのでサードパーティのライブラリよりも大分信頼できる。

Puppeteer はフラグ1つ変えるだけでヘッドレスで動作するかどうかを切り替える事ができるので、 開発中は非ヘッドレスでやりながら、うまくいったらヘッドレスにして Linux サーバで cron で動かすとかができてだいぶ便利 (のはず)。 ちなみに自分の環境は Windows 10 で元々 Chrome を入れてたので使わなかったけど、 Puppeteer 自体に Chrome をダウンロードしてくる機能とかもあるらしい。

ということで冒頭のスクリプトを Puppeteer で書き直してみた。 Node v8.10.0 で動作確認済みです。

github.com

Puppeteer の API

詳しくはこの辺を見てください。

スクリプトの解説

すみません、力尽きました。

すでに色んなブログとかで Puppeteer の解説されてるので省略。

あとでやるかも

楽天 e-NAVI (楽天カードの明細取得) とかもスクレイピングしたかったんだけど、そもそも e-NAVI は CSV で明細を取得できるのでログインしてそれをダウンロードすればええやん、 と思ったら CSV 取得を Fetch API でやろうとしてうまくいかなかった (HTML が返ってきてしまう)。 ちなみにダウンロードの URL はこれ。ログインしてれば CSV がダウンロードされるはず。

https://www.rakuten-card.co.jp/e-navi/members/statement/index.xhtml?downloadAsCsv=1

誰か pull request 送ってください。

参考エントリ

tech.vasily.jp