アプリを作る
文書を扱う
アプリから文書をアップロードし、全文検索で探し、ページの本文を読み、抜き出した値を確かめる方法です。
このページでは、アプリから文書サービスを使うコードを説明します。文書は原本とバージョンで保存され、マルウェアの検査と検索の索引づくりは非同期で進みます。考え方は 文書 にあります。
受け付ける文書
Section titled “受け付ける文書”- PDF と UTF-8 のテキスト。Office の書類(Word・Excel・PowerPoint)は原本のまま保管し、索引と抽出には PDF に変えて使います。
- 1 ファイル 10 MiB まで。ZIP を送ると、中のファイルを 1 件ずつ文書にします。
- 公開範囲は、既定では本人だけ(
private)です。チームで使う文書はprojectにします。 - 同じ公開範囲に同じ中身(SHA-256 が同じ)の文書があれば、新しい文書を作らずに受け取りの記録だけを足します。
アップロードする
Section titled “アップロードする”アップロードは、準備・原本の送信・確定の 3 段です。BFF でブラウザから受けた本文の SHA-256 を計算してから送ります。
-
準備する
const digest = new Uint8Array(await crypto.subtle.digest("SHA-256", body));const sha256 = Array.from(digest, (b) => b.toString(16).padStart(2, "0")).join("");const session = await unwrap(hane.POST("/v1/documents/upload-sessions", {body: { filename: "lease-C-1001.pdf", content_type: "application/pdf", size: body.byteLength, sha256, visibility: "project" },}),);既存の文書に新しいバージョンを足すときは、
document_idも渡します。 -
原本を送る
await unwrap(hane.PUT("/v1/documents/upload-sessions/{id}/content", {params: { path: { id: session.id } },body: "",bodySerializer: () => body, // バイト列をそのまま送るheaders: { "content-type": "application/pdf" },}),);Content-Type は準備で指定した
content_typeと同じにします。 -
確定する
const done = await unwrap(hane.POST("/v1/documents/upload-sessions/{id}/complete", { params: { path: { id: session.id } }, body: {} }));console.log(done.document.id, done.version.version, done.deduplicated);ZIP のときは
completeの代わりにPOST /v1/documents/upload-sessions/{id}/expandを呼び、ファイルごとの結果(受け付けた・まとめた・受け付けなかった)を受け取ります。
確定の時点で、検査は予約されただけです。バージョンの 2 つの状態を見て、画面の出し方を決めます。
| 状態 | 値 | 意味 |
|---|---|---|
inspection_status |
queued・scanning・clean・infected・failed |
マルウェアの検査。clean の原本だけをダウンロードできる |
index_status |
queued・indexed・needs_ocr・needs_conversion・failed |
検索の索引。indexed になったら探せる |
UTF-8 のテキストは確定と同時に索引に入り、検査を待たずに探せます。文字を埋め込んだ PDF は、本文を取り出した後に探せます。スキャンした PDF は OCR を設定した環境で読み、設定がなければ needs_ocr のままです。
const v = await unwrap(hane.GET("/v1/documents/{id}/versions/{version}", { params: { path: { id, version } } }));if (v.inspection_status === "clean") { // GET /v1/documents/{id}/versions/{version}/content で原本を取れる(documents:download が要る)}const hits = await unwrap(hane.POST("/v1/documents/search", { body: { query: "C-1001 満了", limit: 20 } }));for (const h of hits.items) console.log(h.title, h.page_start, h.snippet);- 空白で区切ったすべての語を含む箇所(ページの中の 800 文字のまとまり)を、一致の強い順に返します。語は 8 つまでです。
- 読める文書のいまのバージョンだけを返し、感染を検出したバージョンは返しません。
- 副作用がないので、Idempotency-Key は要りません。
mode で探し方を選べます。
| mode | 探し方 | 提供状況 |
|---|---|---|
keyword(既定) |
全文検索 | 使える |
summary |
ページごとの要約の索引。ページをまたいだ語も見つかる | 使える |
semantic・hybrid |
意味検索と、全文検索との組み合わせ | プレビュー 有効にしていない環境では CONFLICT |
ページの本文を読む
Section titled “ページの本文を読む”当たったページの本文は、原本を渡さずに読めます。
const pages = await unwrap( hane.GET("/v1/documents/{id}/versions/{version}/pages", { params: { path: { id: hit.document_id, version: hit.version }, query: { from: hit.page_start, to: hit.page_end } }, }),);抜き出した値を確かめる
Section titled “抜き出した値を確かめる”抽出の定義(ExtractionSchema)を置くと、文書から型の項目の値を抜き出して、抽出の候補にします。候補は人が原本と並べて確かめると、台帳の値(確定)になります。
apiVersion: documents/v1alpha1kind: ExtractionSchemametadata: name: lease-contractspec: target: LeaseContract review: assignment: fleet-desk documents: types: [lease-contract] fields: contract_no: { labels: [契約番号] } start_date: { labels: [リース開始日, 開始日] } term_months: { labels: [リース期間, 契約期間] }// 要確認の候補const facts = await unwrap(hane.GET("/v1/documents/facts", { params: { query: { status: "needs_review" } } }));
// 候補の値で確定する(value を渡すと直してから確定する)await unwrap( hane.POST("/v1/documents/facts/{id}/confirm", { params: { path: { id: fact.id }, header: { "If-Match": ifMatch(fact.version) } }, body: {}, }),);退けるときは /reject、書類から取れず相手に問い合わせるときは /inquire を使います。どれも利用者のトークンで呼び、確かめた人として利用者が記録されます。