Kindleでセールをしていたので買って読んだ。
SQLを書いてみましょう、分解法的な手法でテーブル設計をしてみましょう、みたいなところはだいたい知ってる内容だった。データベースを使う側としてのことよりも、運用する側としてのことの方があまりよく知らなかった。トランザクションとかバックアップの話あたりは一読しておくとよいと思う。
取っかかりとして読むのにはよいと思うけど、この本だけでデータベースの全てを理解するというわけにもいかない。もっと深い内容については参考文献をあたるとよさそう。
Kindleでセールをしていたので買って読んだ。
SQLを書いてみましょう、分解法的な手法でテーブル設計をしてみましょう、みたいなところはだいたい知ってる内容だった。データベースを使う側としてのことよりも、運用する側としてのことの方があまりよく知らなかった。トランザクションとかバックアップの話あたりは一読しておくとよいと思う。
取っかかりとして読むのにはよいと思うけど、この本だけでデータベースの全てを理解するというわけにもいかない。もっと深い内容については参考文献をあたるとよさそう。
モジュールが対応しているランタイムの最小バージョンの統計に興味があったので調べてみた。他のプログラミング言語だとどういう分布になるのだろうか。EoLが明確に設定されている言語だと傾向が変わってきそう。言語とエコスシテムの未来を考える一助になる?
全自動Test2化ツールのテストを全く書いていないことを思い出したので、整えてCPANに公開した。テスト中で system()
関数を使ってテストが落ちまくっていたので、CLIをラップするクラスを書いたり、proveコマンドを叩くのではなくApp::Proveを使うようにしたり、と工夫が炸裂してなんとかなった。
比較的ずっとコードを書いている一日だったと思う。またもやPARTY☆NIGHTブームが来ている。
CPANに上がっているモジュールが対応しているPerlの最小バージョンの傾向が知りたくなったので、調査することにした。直感では、最低でもPerl 5.8に対応しているモジュールが最も多いと思っていた。
metacpanにはAPIがある*1ので、これを叩いて情報を取得する。
以下のコマンドで、この記事を書き始めた日までの1年間 (2020/5/4 - 2021/5/4) のリリースのうち、最新バージョンのリリースについて検索した。
$ curl -XPOST https://fastapi.metacpan.org/v1/release/_search -d @query.json > result.json
query.json
にはこういうクエリを書いた。Elasticsearchのクエリは普段あまり手書きしないのでちょっと苦労した。
{ "query": { "bool": { "must": [ { "term": { "status": "latest" } }, { "range": { "date": { "gte": "2020-05-04T00:00:00", "lt": "2021-05-04T00:00:00" } } } ] } }, "sort": [ { "date": "asc" } ], "size": 5000 }
2020/5/4 から 2021/5/4 までの間のリリースが3949件見つかったので、これらについて分析する。
以下の jq
コマンドで、各モジュールが対応しているPerlの最小バージョンを列挙できる*2。
バージョンが指定されていない場合は null
が返るので // empty
で除外している。
$ jq -r '.hits.hits[]._source.metadata.prereqs.runtime.requires.perl // empty' result.json
3949個のモジュールのうち、Perlの最小バージョンが指定されているものは2695個あった。
めでたく2695個のモジュールが対応しているPerlの最小バージョンを抽出することができたが、現状では 5.010001
や v5.10.1
といった表記が混在しており、扱いづらい。
version
モジュールを使うことで、こういったバージョン表記をパース・正規化できる。metacpanでも使われている*3。
バージョン表記を正規化してパッチバージョンを落とした上で、辞書順ではなくバージョン順に並べ替えて、個数をTSV形式で出力する。
use strict; use warnings; use feature 'say'; use version; use List::UtilsBy qw(count_by sort_by); my @versions; while (my $line = <>) { chomp $line; my $version = version->new($line); my $normalized = $version->normal; # パッチバージョンを落とす $normalized =~ s/\.[0-9]+$//; push @versions, $normalized; } my %count_of_version = count_by { $_ } @versions; for my $version (sort_by { version->new($_) } keys %count_of_version) { say join "\t", $version, $count_of_version{$version}; }
バージョン指定にミスっているのか Perl 5.100 を要求しているモジュールもあった。Perl 7を要求しているモジュールもあったけど Acme::Postmodern::Perl というAcmeモジュールなので想定した記述のようだった。CPAN Testers Reportが見たことない色合いになっていて面白い*4。
TSV形式で出力することにしたので、さくっとスプレッドシートに取り込むことができる。取り込んで棒グラフを出力してみたものが以下。
*1:実体はElasticsearchのAPIエンドポイント
*2:どのフィールドにPerlの最小バージョンが入っているのかを調べるために、 JSONの所望の値にアクセスするためのキーを逆算する - 私が歌川です のコマンドを使った
*3:https://github.com/metacpan/metacpan-web/blob/4bac58040a7c04f56ceb650f284394b3135c7f9c/lib/MetaCPAN/Web/View/HTML.pm#L81-L87
*4:http://matrix.cpantesters.org/?dist=Acme-Postmodern-Perl+0.11
一昨日の夜から今朝まで2泊3日で市内のホテルに泊まっていた。ホテルのフロントや公園で作業をした結果、かなり捗った。
出かけて帰ってきたら自動的に部屋が掃除されているのがホテルの魅力だと思う。良いことづくめというわけでもなくて、自宅に比べるとインターネット回線が貧弱だった。名前解決に失敗したり、レスポンスが返るのに10秒ぐらいかかったりすることがあった。こういう環境下で満足に働こうとしたら、やりづらいと思う。
引っ越したくなって物件情報サイトを見あさっている。家賃だけなら高くないけど、初期費用を考慮するといきなり高価になって手を出せない。いまの部屋に引っ越したときは敷金ぐらいしか払ってないと思う。敷金礼金以外にもいろいろあるけどこういうものなの?
ちょっとコードを書いて統計を取った。
手元にJSONはあるけど、人間がパースするには大きくて、ぱっと見では所望の値を取得するキーが分からない。値を取得するために人間が試行錯誤しまくるのは不毛なのでもうちょっとなんとかしたい。
id:itchyny さんにjqでキーを逆算する方法を教えてもらった。以下で値にアクセスするためのキーの配列 (.foo[1].bar
なら ["foo", 1, "bar"]
) の列が得られる
$ jq -r -c --arg value foo --stream 'select(.[1] == $value)[0]'
あとはうまく文字列結合をしたりエスケープしたりすれば完成する。
2021/5/3 追記: tojson
でエスケープする方法*1も id:itchyny さんに教えてもらったので反映しました。
$ jq -r -c --arg value foo --stream 'select(.[1] == $value)[0] | map("[\(tojson)]") | "." + join("")'
jqで実現する方法がおそらくありそう、と思いつつ慣れたプログラミング言語で書いてしまったので、jqで実現できることが分かってよかった。
以下は試行錯誤の跡です。
与えられたJSONの、与えられた値にアクセスするためのキーを逆算するスクリプトを書いた。やっていることは深さ優先探索で、発見したキーを順に全て返す。キーに含まれる文字列によって場合分けするのが面倒なので全部クォートしている。
得られたキーを jq
コマンドにそのまま投げることができる。キーから配列のインデックスを消してjqに投げるとうまく列挙できて便利。
import sys import json def main(): input_json = json.load(sys.stdin) target_value = sys.argv[1] found_keys = find_keys_of(input_json, target_value) if found_keys: print('\n'.join(found_keys)) else: print('not found', file=sys.stderr) sys.exit(1) # 面倒なので全部クォートする def encode_json_key(key: str): replaced = key.replace('"', r'\"') return f'["{replaced}"]' # sys.argv[1] はstr型なので、よしなに比較する (うまくいかないこともあるかも) def equal(json_value, input_value): if json_value == input_value: return True else: try: return json_value == float(input_value) except ValueError: pass return False def find_keys_of(data, value): found_keys = [] def _find_key_of_list(xs, value, key): for idx, v in enumerate(xs): next_key = f'{key}[{idx}]' _find_key_of_data(v, value, next_key) def _find_key_of_dict(dic, value, key): for k, v in sorted(dic.items(), key=lambda x: x[0]): next_key = f'{key}{encode_json_key(k)}' _find_key_of_data(v, value, next_key) def _find_key_of_data(data, value, key): if isinstance(data, list): _find_key_of_list(data, value, key) elif isinstance(data, dict): _find_key_of_dict(data, value, key) elif equal(data, value): found_keys.append(key) _find_key_of_data(data, value, '.') return found_keys if __name__ == '__main__': main()
こういう感じで使うと、 "foo"
という値を持つキーを探索して列挙してくれる。けっこう便利だと思う。
$ python3 find_key_of_value.py foo < input.json
true
とか false
は検索できないけどまあいいか。
*1:sub だけだと正しくエスケープするのが大変
連休中に終わらせたいタスクをなんとか終わらせた。外に出て、屋根のあるところでコードを書いてみると意外と捗る。インターネット回線は貧弱だけど。
ワンルームマンションで暮らしていると、家から出ずに別の場所で仕事をしてみる、というのは不可能だなと痛感する。次に引っ越すなら2部屋以上は欲しい。
気分を変えられない、変える手段がない、というのは苦しいものである。