コンテンツにスキップ

capture_targets

撮る対象の一覧。 capture-ledger が答える唯一の問い「どの URL を撮るか」の答えがここに あります。 POST /api/crawls に fromTargets を渡すと、この表の有効な行(呼び出し元の組織のぶんだけ)がクロールの起点になります。

.addColumn("url", "text", (col) => col.notNull().check(sql`url <> '' AND url = btrim(url)`))
.addColumn("url_hash", sql`bytea`, (col) =>
col.generatedAlwaysAs(sql`digest(url, 'sha256')`).stored(),
)
.addColumn("labels", sql`text[]`, (col) => col.notNull().defaultTo(sql`'{}'::text[]`))
.addColumn("enabled", "boolean", (col) => col.notNull().defaultTo(true))
.addColumn("created_at", "timestamptz", (col) => col.notNull().defaultTo(sql`now()`))
.addColumn("updated_at", "timestamptz", (col) => col.notNull().defaultTo(sql`now()`))
列 要点
id BIGSERIAL。投入順で、ローダの ORDER BY がそれを保つ
url CHECK (url <> '' AND url = btrim(url)) と CHECK (url ~* '^https?://') — 空文字・前後空白・http(s) でない値をデータベースが拒否する
url_hash 生成列。digest(url, 'sha256') を stored 保存。ユニーク索引の土台で、直接読むことはない
labels TEXT[]。もう読んでいない —— 起点にするのは url だけなので、BrowserHive にも成果物のファイル名にも届かない
enabled 部分索引 capture_targets_enabled_id_idx が覆う。無効行はコストにならない
org_id この URL がどの組織のものか。既定値は無い —— INSERT では必ず書く。fromTargets はこれで絞るので、他テナントの対象は起点にならない
created_at / updated_at now() 既定。自動更新トリガは今のところ無い
capture_targets_pkey PRIMARY KEY (id)
capture_targets_org_url_hash_key UNIQUE (org_id, url_hash) -- 同じ組織に、同じ URL は 2 度入らない
capture_targets_enabled_id_idx (id) WHERE enabled -- 部分索引

capture_targets_enabled_id_idx が部分索引なのは、読み取りが必ず WHERE enabled を 伴うためです。無効な行まで索引に入れても場所の無駄になります。

ターミナルウィンドウ
pnpm run targets add https://example.com/ --org acme

この表に直接書く、開発用の CLI です。 DB の側では、同じ組織に同じ URL の行を 2 本目に入れようとすると、capture_targets_org_url_hash_key で弾かれます。 別の組織なら、同じ URL を入れられます。 CLI のほかの使い方と、手で足す SQL は、URL ソースにあります。