| @Mac/iPhone

プログラミングをほとんどやらなくなったので iPad Air でよいだろうと思って買ってみたところ、結構使いづらい。

普段 US キーボードを使っているので何の気なしに US 配列の Smart Keyboard Folio ( MU8G2LL/A )を選んだが、日本語と英語の切り替えがハチャメチャにやりづらい。 macOS 標準の日本語入力も ATOK も Ctrl + Shift + j で日本語に、 Ctrl + Shift + ' で英語に切り替えるデフォルトショートカットキーが割り当てられているので、このショートカットで日本語・英語を切り替えていた。 iPad でもこのショートカットで切り替えたいが残念ながら使えない。 JIS キーボードであればおなじみの 英数かな キーがあるのでより簡単に日本語・英語を切り替えられる。

Smart Keyboard Folio のキーピッチの狭さも厳しい。 Apple キーボードぐらいの使い心地を想像していたが、各種キーが小さくなっておりブラインドタッチできない。ふむーという感じ。

Apple のサイトを見ると iPad Pro の 12.9 インチ版はフルキーピッチになってるようなので、プログラミングをしない場合であってもキーボードを使ってそれなりに文章を書くつもりなのであれば iPad Pro 大を選ぶべきであった。

しかし iPad Pro 大と Magic Keyboard の組み合わせとなると ¥172,800 + ¥53,800 = ¥226,600 となり、軽く MacBook Air くらい買えてしまう値段になる。重さ的にも MacBook Air の方が有利そうだ。

iPad Air であれば、コンテンツの消費をメインにしつつ、ある程度はブログ書きもこなせて自分のニーズにマッチするかと期待していたが、正直なところ微妙なようだった。

| @Mac/iPhone

iPad Air (第 5 世代)を買った。

10 年くらい前に第 3 世代の iPad を持っていたが、当時はあまりしっくりこなくてほどなくして使わなくなってしまった。当時の自分のなかの結論としては iPad は様々なアプリからピロピロ通知が来て気になるし、プログラミングできないし、キーボードがないからブログ書きにも使えないし自分にはノートパソコンの方が向いているというのものだった。

しかしプログラミングは卒業してしまったし、今は Apple からカバーと一体となったキーボードも発売されていて、ブログ書きにも使えそうな雰囲気になってきた。 Apple Pencil にも興味がある。そして何より一番気になっている点は iPad 版 Kindle アプリの出来だ。

iOS の Kindle アプリは比較的よくできているのに Mac の Kindle アプリは本当に出来が良くない。クラッシュしまくるし、線を引こうとすると余計なところに引かれてしまうし、使っていてストレスしか感じない。 App Store でのレビューを見ると iPad OS 版の Kindle アプリの評価は悪くないようなので、リストカット感覚で iPad Air を買って iPad 版の Kindle アプリを試してみることにした。

この記事は早速 iPad Air から書いているが、変換に慣れている ATOK を使えないこと以外は特に問題がない。キーボードは一世代前の Smart Keyboard Folio の US 配列( MU8G2LL/A )をフリマサイトで安く購入した。 iPad Air はカメラが一つしかないので現行の iPad Pro 向けに作られた穴が大きい Smart Keyboard Folio よりも一世代前のバージョンの方が向いている。ちゃんと使えるかは reddit で調べて確認した。

これで寝床でゴロゴロしながらブログを書けるようになったので、これまで以上に駄文を濫造していけそうだ。

| @技術/プログラミング

MySQL だけでお手軽に全文検索ができるということを知らなかった。 MySQL 5.6 から入っていたようだった。 Tantivy および Tantiny を使ったやり方を以前記事に書いてサイトで実装しているが、 MeCab によるトークナイズでは二文字の熟語がセットになって四文字になっているようなパターンを取り逃すことがあった(「関連記事」は「関連」と「記事」に分割され、「関連」や「記事」というキーワードで検索したときにはヒットするが「関連記事」で検索するとヒットしない)し、記事追加時の検索インデックス更新処理が不要( MySQL にレコードが追加されたときに勝手に更新される)なので試してみることにした。

やり方は以下の記事を参考にした。

最初にデータベースに全文検索用のインデックスを作成した。

ALTER TABLE `entries` ADD FULLTEXT INDEX index_entry_fulltext(title, body) WITH PARSER ngram;

その後、検索部分のコードを書き換えて以下のようにした。

class Entry < ActiveRecord::Base
  scope :search,
        ->(words) {
          return all if words.blank?
          where('MATCH (entries.title, entries.body) AGAINST (? in BOOLEAN MODE)', words)
        }
end

めっちゃ簡単。

このブログは記事数が 1500 記事くらいなのでぶっちゃけ LIKE 検索でも実用的な速度( 100msec 以内)で結果を取得できるが、 FULLTEXT インデックスを使うと 10msec 程度で結果を取得できる。

ただし Tantivy と比べて劣る点もあって以下は注意が必要。

  1. なぜかわからないが Vim で検索すると何もヒットしない。また Rails で検索すると Rails について触れていない記事もヒットする。 ngram によるインデックスというのはこんなものなのかもしれない。検索ワードが日本語のときはいい感じに結果が表示される。
  2. 複数のテーブルにまたがるデータを一個の検索インデックスにまとめることができない。例えば Tantivy のインデックスは記事のタイトル、本文、カテゴリー、タグをインデックス対象としているが、 MySQL の FULLTEXT インデックスだとテーブルごとにしかインデックスを作れないので(当たり前)、複数のテーブルにまたがる検索をするときにはテーブルを JOIN するしかない。 OR マッパーを使っている場合には利用しづらい。

1 の問題に関しては、 MySQL 5.7 からインデックス生成時の PARSER に MeCab などを指定できるようになったのでそうすると回避できるかもしれない。ただし MeCab のインストールや設定を行う必要があるので要注意。

2 の問題に関しては全文検索システムを入れた方が良さげ。 Tantivy であれば非常に簡単に導入できる。

現状、このサイトでは右上の検索窓から検索したときのインクリメンタルサーチとアーカイブページでの絞り込みは Tantivy を、インクリメンタルサーチの結果で必要な情報が得られなかったときの「全文検索する」と 404 Not Found ページの検索は MySQL の全文検索を使うようにしている。

二つの検索

| @ブログ

404 ページ、昔はそもそもなくて 404 Not Found ステータスを返すだけだったり、あっても「見つかりません」というだけのものが多かったけど、最近はサイトマップ的なコンテンツや代替となるコンテンツを表示するサイトも見かける。というわけでこのサイトでもやってみることにした。

このブログの URL は /YYYY/MM/DD/slug という形式になっている。パスの /YYYY/MM/DD の部分はお飾りで、実際は slug がユニークになっているので slug で表示すべき記事を判定している。

よくあるのが記事を公開後、 slug 部分にタイポを見つけて変更するというケース。しかしすでにその時点で記事が Twitter などでバズってたりすると、 Twitter で共有されている記事を見てやってきた人が 404 Not Found ページを見ることになる(この前の「不便になるインターネット」がまさにそうだった)。それはまずいので slug のタイポを修正すると同時に Nginx の設定ファイルをいじってタイポ修正前の URL から修正後の URL へリダイレクトするようにしていた。しかしリダイレクトごときでサーバーの設定ファイルを修正して root 権限でリロードするというのはめんどい。 SSH でログインもしなければならない。大げさすぎる。

というわけで思いついたのがこの機能で、 Ruby でクラス名やメソッド名をタイポしたときに正しい候補を表示する did_you_mean.gem を利用した。存在しない slug で URL を開くと以下のように候補が表示される。

404 Not Found

コードはこんな感じ。

# Helper
def not_found_candidates
  @not_found_candidates ||=
    begin
      slugs = Entry.published.where.not('slug REGEXP ?', '^[0-9]+$').pluck(:slug)
      spell_checker = DidYouMean::SpellChecker.new(dictionary: slugs)
      current_slug = request.path_info.split('/').last
      slug_candidate = spell_checker.correct(current_slug)
      Entry.published.where(slug: slug_candidate)
    end
end

# View
- if not_found_candidates.any?
    %p Did you mean?
    - not_found_candidates.each do |candidate|
      = link_to candidate.title, candidate.link

データベースから slug 一覧を取り出して辞書とし、 DidYouMean::SpellChecker に食わせて似たページの候補を取得して表示する。タイポありのページを訪れた人はワンクリックしなければならないという手間が増えるが、これでタイポを修正したときに面倒なリダイレクトの設定をする必要がなくなった。

なお 404 ページには検索窓や最近の記事、カテゴリー一覧も表示して回遊性を高めている。

404 Not Found ページ

| @WWW

三苫海岸

ソーシャルメディアやニュースサイトに毎日新しいコンテンツが次々に投稿されるので、インターネット上の総情報量は増えていっているはずだが、 20 年前と比べてアクセスできる情報の種類は減っているのではないかと感じる。いま何か情報を得ようとしたときに Google は以前ほど便利ではなくなってきている。 Google がキュレーションした情報にしかアクセスできないからだ。誰にもフィルタリングされていない生の情報にアクセスしようとしたら Twitter 検索の方がよっぼどよいと感じるくらいだ。

昨年末、 40L の登山用バックパックをニュージーランドのショップから購入した。日本でも売っていた商品だが、国内の正規取扱店では売り切れてて個人輸入で購入するしかなかった。商品名で Google 検索しても日本語のページしかヒットしないし、在庫ありとして表示される楽天や Amazon のページには怪しい業者が定価の何倍もの価格でふっかけて販売しているケースがほとんどで、 Google から直接海外のショップのページに辿り着くことができない。メーカーのウェブサイトから各国の取り扱い店をたどってようやく販売しているページを見つけてメールで問い合わせて購入することができた。

15 年くらい前、日本から patagonia.com にアクセスすると patagonia.jp にリダイレクトされて、アメリカで 10000 円くらいで売られているものが日本では 1.5 倍の 15000 円くらいになってて日本人はぼったくられている、というようなことを書いている記事がバズってた(ちなみにいまも patagonia.com を開こうとすると patagonia.jp にリダイレクトされる1)。当時は日本人が価格差に気がつけないようにしているのは邪悪だということで攻撃されていたのはパタゴニアだけだったが、現在では Google が似たようなことをやっていて、インターネット全体でパタゴニアと同じようなことが起こっている。日本人(日本の IP アドレスから日本語設定のブラウザーを利用している人)が海外のショップから直接物を買おうと商品名で Google 検索しても、海外のサイトはほとんどヒットしない。日本人は日本語のウェブページしか見させてもらえない。

日本語のページであっても、すべてが検索結果に表示されているわけではない。何か商品について調べようと Google 検索しても結果に出てくる店は決まっていて、 20 件程度表示されたあとにそれ以降の情報を探すことができない。楽天や Amazon 内の情報のほか、 BASE や STORES 、カラーミー、 Shopify などといった割と利用者が多いカートを採用しているページは Google ショッピングの一覧に表示されるが、そうではない自前の CMS で構築されているような地方のショップのサイトなんかは結果に出てこない。

昨シーズン、ARC'TERYX の Motus AR Hoody というパーカーを買ってとても使い勝手が良かったので、今シーズンも色違いを買おうと探してみたら主要なサイトではすでに売り切れていた。一昨年も GRiPS のサイト(カラーミーで構築されている)に掲載されたタイミングでは買い逃していてネットの海をさまよって何とか辿り着いた地方のショップのサイト(メジャーなカートシステムではない独自システムのサイト)で購入することができた。まさかそんなことあるまいと思いながら今シーズンもそのサイトを訪れて探してみたところ、何とよそでは売り切れて定価の 2 倍とか 3 倍の値段で売られている Motus AR Hoody が定価で販売され在庫が残っていた。こういう例は一度や二度ではなく、何度か経験した。

インターネットに情報が増えすぎて、 Google としても検索結果に表示するページは絞るしかないのだと思う。すると Google にとってクローリングしやすく、サイトの更新を追っかけやすいサイトばかりが検索結果に表示されるようになる。サイトのレスポンスが遅かったり、構造がいまいちイケてないサイトは検索結果に出てきづらくなる。その結果、同じようなページばかりが検索結果に表示され、一部のサイトにだけトラフィックが集中して独自サイトにはアクセスが集まりづらくなってきている可能性がある(だから自分が買ったショップのページでは Motus AR Hoody のような人気商品が売れ残っていた)。

インターネットは情報の非対称性を下げて、より取引を効率化するものだと信じてきてが、どうやらそうではないようだ。一部のページにだけアテンションが集中し、むしろ情報の非対称性が高まっている。あっという間に定価で売られていた商品が売り切れてモノの値段がつり上げられ、一方でアテンションを集められないサイトでは売れ残ってセールになっている。経済学のセオリー通りなら、市場の原理が働いてギリギリに近い競争均衡価格で商品は取引されるはずだが、実際には逆の現象(正規販売店による定価販売と一部の転売業者による価格つり上げ販売)が起こっている。

EC サイトだけでなく、ブログや個人のウェブサイトでも同じような問題が起こっているのではないかと感じる。 Google のコアアルゴリズムアップデートで自分のブログも随分 Google 検索からの流入が減った。

2015 年からの月ごとの検索流入の推移

実際には存在するのに、 Google から不遇されて存在しないことになってしまっているウェブサイトがインターネット上にはきっとたくさんあるだろう。

昔のインターネットのような、検索結果をたどればたどるほど新しい情報と出会えていた頃が懐かしい


  1. 2023-01-18 訂正: patagonia.com から patagonia.jp へのリダイレクトは2023年1月18日時点では機能していなかった 

| @雑談

福岡県水産海洋技術センターから見る脊振山

年が明けてからやるのはどうかと思うが、タイミングがなかったので今さらながら 2022 年のふりかえり。

ランニング

2022 年はよく走った。これまでも何度か走ってると書いていたが、ちゃんとグラフにしてみると 2022 年の 8 月までは大して走っておらず、 2022 年の 9 月から本腰を入れて走るようになったようだ。

オレンジ色の線が 2022 年のもので、9 月から傾きが急になっている。 9 月は月間 110km 走った。月間 100km はフルマラソンに出られる基準のようなのでこの頃は調子こいていた。

ランニングの頻度を上げるのに役立ったのが計画表だった。それまで漫然と走っていたが、漫然と走っていると月間 100km も走れないことに気がついた。自分は一回 5km 走っているが、それを気が向いたときに週 2, 3 度やるだけだと月間 50km くらいにしかならない。きちんとランニングした日と距離を確認していかないと目標には辿り着かない。ログは Apple Watch で取得しているが、統計データとしては見られない。なので HealthFit というアプリを使っているが、 HealthFit では目標設定ができないので Numbers でシートを作って管理することにした。これは元はてなのディレクターの二宮さんの記事の真似。

こちらが年間の週次のランニング計画。

年間ランニング計画

こちらが月間の日次のランニング計画。

月間ランニング計画

月間のシートに日次の目標と実績値を入れると、年間の週次のシートに自動反映される仕組みになってる。これによって自分は一週間に何 km 走る予定で現在目標に対してどのくらい達成しているのかを確認できるようになる。

37signals の本を読んで、計画を立てるのはアホだ、数値目標なんて意味がない、未来を先読みすることはできない、という発想に影響されて計画を立てたりするのは何となく良い印象を持っていなかったけど、月間何キロくらい走りたいとか、ベンチマークとなる具体的な数値目標がないとなかなか実績は積み上がっていかないと思う。もちろんまだ走ったことがない状態でいきなり月間 100km のような目標を立てるのは愚かだと思うが、そこそこ走れるようになってきたら(自分の力がわかるようになってきたら)計画を立てたり数値目標を設定したりするのは悪くないことだと思う。

9 月にがむしゃらに走ったおかげか、最近は走るペースが一段速くなっていて、 1km を 5 分台で走れるようになってきた。今年はちょっと色気を出して初心者向けのトレランの大会に出てみようかと思ってる。

登山

4 月に脊振山系全山縦走、 11 月に九州脊梁に行った。夏に北アルプスを予定していたが、ちょうどコロナにかかって行くことができなかった。何にせよ最近は山に登るのよりも近所を走る方が楽しいので山への足が遠のいた一年だった。登山時の体力作りで走り始めたのに本末転倒している。

仕事

一昨年は結構でかい成果を出せたが 2022 年はぱっとしない一年だった。反省したい。

生活

iPhone を買い換えて 11 から 14 Pro になった。カメラが三つになって望遠の写真を撮れるようになったのが便利。 Dynamic Island も便利。タイマーかけてるときに常に Dynamic Island で残り時間を確認できるのは相当便利。 164800 円払ってよかった( 36 回ローン)。

サウナにハマってぼちぼち行っていた。サウナーの人たちのように一週間に何回も通うというほどではないが、金曜日の仕事帰りにサウナまで走って行ってサウナに入って帰るというのを何回かやった。花金感が出て良い。

2020 年に車を買い換えたがあまりドライブしてない。アメ車なので燃費が悪い、コロナなので出かけづらい、などいろいろあるが、せっかく車を買い換えたのに使わないで置物になってるのはもったいないので有効活用したい。

そういえば 2022 年は YouTube をよく見た。 YouTube Premium に入ったので広告が表示されなくなり、邪魔が入ることなくとてもなめらかに動画を視聴できるようになった。 Netflix はあまり見ていなかったので解約し、 YouTube で素人が上げる動画をよく見た。ストーリーのない、ただ料理をしているだけとか、ただ穴蔵を掘っているだけの 15 分くらいの動画を見るのがちょうどよい。 Netflix の動画は長いし、ことあるごとに金、暴力、セックスを意識させられるので見るのがきつい。こってりしすぎている。

ブログ

Tantivy を導入して全文検索できるようにした。検索も見た目を変えてインクリメンタルサーチできるようにしたりした。年に一回くらいはバズる記事を書きたいが、 2022 年は不作だった。反省したい。

総括

仕事や生活、ブログは本当にぱっとしなかった。その代わり走ることを頑張っているのかも知れない。ランニングや登山は、大して頑張って生きていないのに走ったり山に登ったりするとめっちゃ頑張ってるかのような錯覚を得られて自己肯定感が高まる。仕事などでもちゃんと成果を出しつつ走ったりできたら良いのだけど自分の場合は逃避になってるような気がする。しかし走るのをやめたら仕事で成果を出せるかというとそうでもないし、遺伝的に糖尿病のリスクがあるので発症しないように死ぬまで走り続けるしかない。 2023 年は運動しつつ仕事やブログ書きでも一定の成果を上げたい。あともうちょいサウナに行きたい

| @技術/プログラミング

ブログのアクセス数を集計してランキング(人気記事一覧)を表示している。

シェルスクリプトでログを集計して頑張っているが、ボットからのアクセスを除外など結構やることが複雑化してきた。また最近は主にロシア方面からのスパマーによるアクセスが多く、全然いま読まれる要素がない記事がランキング上位に入ったりしてた。スパマーは以下の 2 記事が好きなようだ。

Google Analytics でアクセス数を見るとこれらの記事は上位に入ってこないので、 Google はちゃんとスパマーからのアクセスを除外しているのだろう。

というわけで Google Analytics の API からアクセス数を取得してみることにした。

しかし調べてみた感じ、あまり情報がない。 Google の公式ドキュメントは Java とPython と Go と PHP と JavaScript のサンプルしかない。

Google が公開している Ruby のライブラリはあるが、ドキュメントがえらく貧弱で勘で使うしかない。

使い方を紹介しているブログもあるにはあるが、この Ruby 製のライブラリはアルファ版とベータ版しかなくてころころ仕様が変わるようだ。先人の情報通りに動かしてみたら全然動かなかった。

API の仕様や上述のライブラリのコードを読みつつ以下のようなコードを書いたところいい感じに使えるようになった。 Ruby で Google Analytics の API にアクセスしたいと思っている人には参考になるんじゃないかと思う。

↑のコードでは metrics は screenPageViewstotalUsers を取得している。 dimension は pagePathpageTitle だ。ほかのが必要であれば変えてあげればよい。これを Rake タスクから呼び出して必要な情報を得るようにしている。

API 呼び出しについては Google が提供している Query Explorer で確認するとよい。

また Analytics API は利用開始前に設定が必要。 Quickstart ページで API を有効化し、 GCP に IAM を作成して credential をダウンロードして Google Analytics 側でこの IAM への API アクセスを許可する必要がある。コード書く前にこの辺でくじけそうになるだろうけど頑張ってほしい。