RORK LABEN
MAXPRICE — Rork Max は月額 $200 です。無料枠は週5プロンプト程度で、App Store へ出すには別途 Apple Developer Program の年 $99 が要りますARR — 2026年2月に登場した Rork Max は、3日で ARR 150万ドルに達したと報告されています。ネイティブアプリ生成への需要の厚さがうかがえますNATIVEAPI — Rork Max は SwiftUI・ARKit・HealthKit・HomeKit・Core ML・Metal といったネイティブ API に直接届くため、React Native では回り道になる領域が射程に入りますSHIP — Xcode を開かずに2クリックで App Store へ提出できる導線が用意されています。署名とプロビジョニングでつまずく段階を飛ばせますCOST — 従来のネイティブ iOS 開発は5,000〜50,000ドル超という見積もりが一般的で、月額 $200 はその比較軸の上で評価されていますSDK55 — Expo SDK 55 は React Native 0.83 と React 19.2 を採用し、レガシーアーキテクチャのサポートを打ち切りました。New Architecture が唯一の選択肢になりますMAXPRICE — Rork Max は月額 $200 です。無料枠は週5プロンプト程度で、App Store へ出すには別途 Apple Developer Program の年 $99 が要りますARR — 2026年2月に登場した Rork Max は、3日で ARR 150万ドルに達したと報告されています。ネイティブアプリ生成への需要の厚さがうかがえますNATIVEAPI — Rork Max は SwiftUI・ARKit・HealthKit・HomeKit・Core ML・Metal といったネイティブ API に直接届くため、React Native では回り道になる領域が射程に入りますSHIP — Xcode を開かずに2クリックで App Store へ提出できる導線が用意されています。署名とプロビジョニングでつまずく段階を飛ばせますCOST — 従来のネイティブ iOS 開発は5,000〜50,000ドル超という見積もりが一般的で、月額 $200 はその比較軸の上で評価されていますSDK55 — Expo SDK 55 は React Native 0.83 と React 19.2 を採用し、レガシーアーキテクチャのサポートを打ち切りました。New Architecture が唯一の選択肢になります
記事一覧/開発ツール
開発ツール/2026-08-02上級

2文字の検索語が静かに0件になる — Expo アプリの日本語検索インデックスを実測で選び直す

SQLite FTS5 の trigram トークナイザは2文字以下の日本語クエリを例外なしで0件にします。2万件のカタログで線形走査・bigram 転置索引・FTS5 を実測し、索引を足すべき閾値と起動コストの実数を出した記録です。

Rork525Expo154SQLite4FTS5日本語検索パフォーマンス32

プレミアム記事

朝、家族から「猫の壁紙が1枚も出てこない」と言われました。

自分の端末で確かめると、たしかに「猫」と打った瞬間に結果が空になります。カタログには猫のタイトルが600枚以上あります。エラーログには何も残っていません。クラッシュもしていません。ただ、0件。

原因は、その前の週に私自身が入れた検索インデックスでした。件数が増えてきたので「そろそろ線形走査は限界だろう」と考え、SQLite の FTS5 に載せ替えたところだったのです。速くはなりました。ただ、日本語で最も打たれやすい長さのクエリが、まるごと落ちていました。

この記事は、その後で腰を据えて計測をやり直した記録です。線形走査・bigram 転置索引・FTS5 trigram の3つを同じデータで走らせ、どの規模でどれを選ぶべきかを実数で決め直しました。結論から言えば、私は索引を捨てて線形走査に戻しています。

計測の土台 — 再現可能なカタログを作る

実データをそのまま貼ると再現できないので、壁紙カタログを模した擬似データを決定論的に生成しました。タイトルは「修飾+被写体+接尾」、タグは3つ。1件あたり正規化後およそ13〜20文字という、実物に近い分布になっています。

// gen.mjs — 線形合同法で決定論的に生成(同じ seed なら同じカタログ)
let seed = 20260802;
const rnd = () => (seed = (seed * 1103515245 + 12345) & 0x7fffffff) / 0x7fffffff;
const pick = (a) => a[Math.floor(rnd() * a.length)];
 
const subj = ["夜景", "桜", "富士山", "海", "星空", "猫", "森", "雪原", "花火", "滝", "紅葉", "銀河"];
const mod  = ["静かな", "淡い", "鮮やかな", "霧の", "真夜中の", "冬の", "黄昏の", "雨上がりの"];
const suf  = ["の風景", "のシルエット", "のグラデーション", "のパノラマ", "", ""];
const tagp = ["ミニマル", "ダーク", "パステル", "モノクロ", "和風", "自然", "高解像度", "縦向き"];
 
export function makeCatalog(n) {
  const out = [];
  for (let i = 0; i < n; i++) {
    out.push({
      id: "w" + i,
      title: pick(mod) + pick(subj) + pick(suf),
      tags: [pick(tagp), pick(tagp), pick(tagp)],
    });
  }
  return out;
}

正規化は3層です。NFKC で全角半角を潰し、小文字化し、カタカナをひらがなへ寄せ、空白を落とします。表記ゆれの吸収そのものは以前まとめたとおりですが、今回はこの関数の実行コストが主役になります。

const norm = (s) =>
  s
    .normalize("NFKC")
    .toLowerCase()
    .replace(/[ァ-ヶ]/g, (c) => String.fromCharCode(c.charCodeAt(0) - 0x60))
    .replace(/\s+/g, "");

計測環境は Linux コンテナ上の Node 22.22 と Python 3.10(SQLite 3.37.2)です。手元の iPhone ではありません。以下の数値は絶対値ではなく比率と桁を読むためのものとして扱ってください。実機は一般に数倍遅く、私の手元の検証では3〜5倍を見込むと大きく外れませんでした。

素朴な線形走査は、思っていたよりずっと遠くまで走る

まず「何もしない」実装を測ります。読み込み時に正規化済みのキー配列を作り、クエリごとに indexOf で舐めるだけの実装です。

// 正規化済みキーを平坦な文字列配列で保持する
const keys = new Array(items.length);
for (let i = 0; i < items.length; i++) {
  keys[i] = norm(items[i].title + items[i].tags.join(""));
}
 
function search(q) {
  const nq = norm(q);
  const hits = [];
  for (let i = 0; i < keys.length; i++) {
    if (keys[i].indexOf(nq) >= 0) hits.push(i);
  }
  return hits;
}

8種類のクエリを各20回、中央値を取った結果です。比較のため { id, k } のオブジェクト配列に対する filter も並べました。

件数平坦な文字列配列オブジェクト配列 + filterキー保持量(概算)
20,0000.80 ms1.11 ms1.4 MB
50,0001.99 ms2.79 ms3.6 MB
100,0004.05 ms6.48 ms7.2 MB
200,0008.50 ms12.43 ms14.4 MB
500,00020.21 ms29.65 ms35.9 MB

60fps のフレーム予算は16.7ms です。この環境では20万件まで1フレームに収まりました。実機の3〜5倍という補正を掛けても、数万件規模なら線形走査で十分に戦えます。

同じデータ・同じ判定でありながら、平坦な配列とオブジェクト配列で1.4倍の差が出ている点は覚えておく価値があります。プロパティアクセスが1段挟まるだけの違いですが、走査回数が件数分あるので効いてきます。索引を設計する前に、まずここを平坦にするほうが費用対効果は高いです。

私が最初に索引へ手を伸ばした判断は、この表を作る前のものでした。「2万件は多い」という体感だけで動いていたわけです。

ここまでお読みいただきありがとうございます。

この記事の続きを読む

この先には、実装コードやベンチマーク結果など、実務でお役に立てる内容をご用意しています。このサイトは広告を掲載しておらず、サーバーや開発にかかる費用はメンバーの皆様のご支援で成り立っています。もしお役に立てていましたら、ご支援いただけますと大変ありがたいです。

この記事で得られること
FTS5 の trigram は2文字以下のクエリで例外を投げず0件を返す。実測では「猫」606件・「夜景」816件・「桜」1,263件が静かに消えた
20,000件の線形走査は中央値0.80ms。索引を足す判断は「件数がフレーム予算を割ったとき」に限ってよい
本当のボトルネックは検索ではなく起動時の正規化パス。20,000件で39.5ms、事前正規化と改行連結の配布で1.1msまで縮んだ
Stripe による安全な決済 · いつでもキャンセル可能

この記事を購入する

この先の内容をすべてお読みいただけます。一度のご購入で、いつでも何度でもアクセスできます。このサイトは広告を掲載しておらず、皆さまのご支援がサーバー費用などの運営を支えています。

または
メンバーシップなら全記事が読み放題 →
シェア

お読みいただきありがとうございます

Rork Lab は広告なしで運営しており、サーバー費用などの運営コストはメンバーシップのご支援で賄っています。実装コード・ベンチマーク・本番設計パターンなど、実務でお役立ていただける記事を毎日更新しています。もし読んでよかったと感じていただけましたら、ぜひご覧ください。

  • コピー&ペーストで使える実装コード付き
  • 毎日新しい上級ガイドを追加
  • ¥580/月 または ¥1,480 の永久アクセス
メンバーシップを見る →

関連記事

開発ツール2026-07-10
React Compiler を Expo に入れて、手書き memo を 41 箇所消しました
Rork が生成した React Native 画面に React Compiler を導入し、再レンダリング回数を Profiler で実測しました。手書き memo/useCallback の削除判断、効かない箇所の見分け方、CI での回帰検知までをまとめています。
開発ツール2026-04-12
Rorkアプリが重い・遅い・フリーズする原因と解決法:パフォーマンストラブルシューティングガイド
Rorkで作ったアプリが重い・画面切替が遅い・フリーズするときの原因を体系的に診断し、実践的な解決策を解説します。React Native特有のパフォーマンス問題にも対応。
開発ツール2026-07-30
Renovate が触ってよい依存と、触ってはいけない依存 — Expo SDK 57 の123パッケージを機械的に切り分ける
Rork 生成アプリに依存の自動更新を入れると、Expo が版を管理している123パッケージまで巻き込まれます。2026年7月30日時点の実測では、うち6件が npm の最新版へメジャー更新される差でした。除外リストを手で書かず SDK から機械生成する手順をまとめます。
📚RECOMMENDED BOOKS
大規模言語モデル入門
山田育矢
LLM開発
生成AIプロンプトエンジニアリング入門
我妻幸長
プロンプト
Claude CodeによるAI駆動開発入門
平川知秀
AI駆動開発
※ アフィリエイトリンクを含みます
もっと見る →