はじめに

IIIF (International Image Interoperability Framework) は、デジタルアーカイブや美術館のコレクションで広く使われている画像配信の国際規格です。IIIF Content Search API を使うと、マニフェスト内のアノテーション(注釈やタグ)を検索できます。

しかし、IIIF Content Search API は通常、サーバーサイドでの実装が前提となっており、静的サイト(GitHub Pages、Vercel、Netlify など)では実現が難しいとされてきました。

本記事では、Service Worker を使ってクライアントサイドで IIIF Content Search API を実装する方法 を紹介します。この手法により、静的サイトでも Mirador などの IIIF ビューアで検索機能を利用できるようになります。

課題

従来のIIIF Search APIの仕組み

[Mirador]GET/search?q=keyword[]JSON

IIIF Content Search API は、クエリパラメータ(?q=検索語)を受け取り、検索結果を JSON で返すエンドポイントを必要とします。これは動的なサーバー処理を前提としています。

静的サイトの制約

静的サイトでは:

  • クエリパラメータに応じた動的なレスポンスを返せない
  • サーバーサイドの検索処理を実行できない
  • 静的 JSON ファイルしか配信できない

解決策:Service Worker によるリクエストインターセプト

Service Worker は、ブラウザとネットワークの間に位置するプロキシとして機能します。これを活用して、検索リクエストをインターセプトし、クライアントサイドで検索処理を行います。

アーキテクチャ

[[[MSMieirrravadidoGcorEeJIr]TaI]WvI/oaFiriSikncCiedrofrein/]xpts.teijntsteo/nSseeaarrfccehht/cAihPnIdex.json?q=keyword

実装

1. 検索インデックスの生成(ビルド時)

まず、アノテーションデータから検索インデックスを生成します。

f}unca}r}bcon)e;utnne};t'tteiison)u@yonlottttlmacmt;rcpttdnareaonaaanoear-etixntnnnr?:n:lisgnietgiovig{tEeeetos:uvtafe`ce'nsanrn.aaaset$axStreispagttIs:{ntercre.uneiidtcv'aihasfsn:oo:Iaaa:re-tohonndnnscsie=r(.a:I`:nvI'h:nSE{bnd$oadhIde[aona:{`.stneea]cdono$tItdnxr;hy.n`p{adpet.c(_bo$tor}:xrjhavo.{ipg#/'isInadmootex,ennlyopnityisdou_ttso_wi.eelii.nrhilx=,avobse=fe(>nana.g$.nagtssbi{ign{ui.eaoaotnaobUsnn/hognarena,te,slUopa,e}r.itU/lt/irc}asola/ren}nmgas/vaer,aantcnsi_honfr/p$ee2tt{sg/iaaticotn.ooninjnnsoos}t)n.o`ecnx{$a`t{n,.avjnasnsoo_n.i'in,dd}e`x,}`,

生成される index.json:

{}""""]@ttecyon{}opttnear"""""""t"litlmacmte:Eeeaonaaaxnsxntnnnrt"t"tgiovig"Sr:"uvtafe:ei:aaasetae[gttIs""rs"eiidt:hc""oo"Ith::nn:d"tI"I"hpn1":d":t:d2j"ht/e,1a":t"p/x6"cth:i",o"pt/i,mh:t/imt/pefet/:x.npe/ait:x/moi/aep/n/mxlagepaep"xlm.i,aepc/m.lospcemelo./aemccr"./oac,ccmnhoa/v/mnma2/vas/aan/cnsi1onf"n1ett"sea,txt.tij.osjnos/no1"n",",,

2. Service Worker の実装

検索リクエストをインターセプトし、クライアントサイドで処理する Service Worker を作成します。

ca}f}f}s}a}ssosuue)seenyi}ccsrncrnr}lci};ycccccr}llisnfooeecoeIce;Ffofnoooooe)ffitcrnnattnt..}Itt'''w}r}h}e.necnnnnnth;..i(essruisufm)Iiu@@@i,e)i)tasI(vsssssueaafsfstttcrotriaemba)Forcitt'ts''mr}o)t'amba)cdtIueftttttradd-eueuhnnnlpnaef;nnodyh@oo@@oe,n,s@naefhdIrnundddsanarriInt(ttftCn'pittuitts'c::tntftEuFltnqiirreEEercrnennieoieercoeof{t:enyardyio@hyocoevr..cunneenrvvactcsddnxrnrnyhrrnoe':plc'pvutarrpthrrelprteddsseseerhihspeedemd(t,:e:trxr:e:e:earyr.eea:e:naeireeupw:nncIoIeoxxecaeer:emte{'s'tcpses't:t=tsoyxxlotthnnnanCxulxnyqna'q':r:r:iee:nu:irrLhpnUtnR{LL-ddrs=a;ti.tutt:use.o:'tlo.r.inno=r=sseiiselececezer=eSec'sren:rrt'nm.aseanhles'sswxoxhahSeny>rSe's:sueno:{.ysssabftwmdaua=DpCtt.CaCI=weedtyeahtAclsta'e..e:teteeWnr=waooeejadana.aQr=(,'artUn:tur:rcntmacfenU.idlaetnnnnscScdaisrui>{,rctrnLslyA.ntaar[horeRitl.`ixasteehehewteceechploa.t.netrrpcr,rrLnhes$teeerreaexathrsehR:,tylsann:yg(h.e((c(Se{c=(n((r.Cir((yne/aee.notC.er:e,'elheauluJt''=chateiitAs/trnmntrottHnfvuaarrotfS-iahacsnn=rPpii'gaoayne=iteednrclaeoOTncnIshfpddyIoio,tptt.tx>trtnedch.dSrNysten(eeoeeq.ninh(aimet'yctslhPoSem.ptiwdi.tnxxutsfLrtoon(,.h.(eRareaaseavengcsU,eee.iintt{a'r'Seriartt'laMxdeherrx(is=o'iAn,e/eqagrcSr:lta(et(.lqytrot>n,vsnqsaumichei'epix(ij,u..e/'IaTo(ueresnh(an','(nUinsettqa,(dteteeacs.}Iirga,)drndoirooup{,ixavsrhtg$nncip;eldennyLLeiottetcR(e{ddhfp(x)ex(d)oos/n'in.hertueeRyleU)xU)ewwts,,otu/qe(rxxe(i=vrUr;x{eeUen)riuq'l(,src>el{rl)rrraIlneuq.ipean)l);CClrd=)dse'pnqostst);aa,c]>;ets)adunpie){;ssh,x(tteesooleer/{.e,hxrennf=((e1jvnUy(s/.>))s/seuar)rejs..uconrml;eDsketilontl'e)qaoivrntn'.);};utnpeicst)r`ea'Wnml)ee{;s)at(ux&qt,}i.)d{t&u.tw;e.eu{iasjusrni(srtlgtnol,,(Uon.)nr'sur)tm,ere;iaalsllr)u(ic)lczh;tlePsida)eQr;nuatemsrs.y.c)hl)aasi(m'(q)'))));{

3. Service Worker の登録

アプリケーション起動時に Service Worker を登録します。

e}xpi}t}}rofrercryccrccrgt(oeooeaoei!nt{nnttntsa(sussucsuts'ortorhoreyslnlnlnrneere(e-cr.fe.te.fivwaglrreaifialiourrliucrssgeorsfnenet(;roe-cW(;r')r;sto'aI(eirStI{'aokeiIRrneroFecrvnghr'iSi-ec=essgieatwinarr.sWwcattnoahtseariirvktSoIieenIgrnrIaavfFtiviaSosiciergela)naer)otWdctoo:h{rr'Ss.k,euserperevprriovrrcrieoetcgrWeei)odWs;r'otk)ree;krree(rd).':r)e;Pgriosmtiesre(<'b/oioiliefa-ns>ea{rch-sw.js');

4. マニフェストへの検索サービス宣言

IIIF マニフェストに検索サービスを宣言します。

{}""""]@itscdye{}o"prn:ev"""""t"i@@@ple":ccitraxheodyobtt""n"pfe"tM:t:eil:pae"l":n[x":e:"/ith"h/f"t":"tee:tSStxspe"epat":aha:m"h/rtr/p,t/ctc/ltehphiepxS:i.:ae/wic/mr/ifo/pit.miliihiieciimi.efn/afc1.an.o"itpiim,ohifo/i/esaspsaeprtpaime.ir/asj/csnessheinoe/aftnairea"rncst,cdhtihe/"o/x1n1.//js3cse/ooacnnrot"cne,htx"et,x.tj.sjosno"n,",

5. Mirador での利用

Mirador の初期化前に Service Worker を登録するだけで、標準の検索機能が動作します。

ie}mxppu}Moos,irrerrttEe[afg]d{dfSi)oeees;rrfcrtMVeatveiigu(irreil(cIawst)eIdetIoref=WFr.ru>oStInresIc{kaxIterFircSohenSaerMrcivhriSacederoWvroiVrcikeeeWwroe(rr)k(;e{rm}anfirfoemst'U@r/lli}b)/r{egister-iiif-search-sw';

動作フロー

1234567891.........0.SMMGSJIeiiEeaIMrrrTrvIivaavaFriddiiSacoicncCderriedrooieinrWfWxpto/o.tersrjnkiksteteorernS/esaeracrhchA/PiIndex.json?q=

メリットと制限

メリット

項目説明
静的サイト対応GitHub Pages、Vercel、Netlify などで動作
サーバー不要すべてクライアントサイドで完結
低コストサーバー維持費が不要
高速インデックスはブラウザにキャッシュされる
Mirador 改修不要標準の Mirador がそのまま使える

制限

項目説明
インデックスサイズ大規模コレクションでは初回読み込みに時間がかかる
HTTPS 必須Service Worker は HTTPS 環境でのみ動作(localhost は例外)
ブラウザ対応IE11 非対応(モダンブラウザはすべて対応)
全文検索の限界高度な全文検索エンジンほどの精度は出ない

大規模コレクションへの対応

インデックスサイズが大きくなる場合は、以下の対策が有効です:

  1. インデックスの分割 : マニフェストごとにインデックスを分割
  2. 遅延読み込み : 検索時に初めてインデックスを取得
  3. 圧縮 : gzip 圧縮でファイルサイズを削減
constindexUrl=U`R/Liiif/${siteId}/3/${itemId}/search-index.json`;

まとめ

Service Worker を活用することで、静的サイトでも IIIF Content Search API を実現できます。この手法のポイントは:

  1. ビルド時に検索インデックスを生成
  2. Service Worker でリクエストをインターセプト
  3. クライアントサイドで検索を実行
  4. IIIF 標準形式でレスポンスを返却

これにより、Mirador などの IIIF ビューアの改修なしに、静的サイトで検索機能を提供できます。デジタルアーカイブの構築において、サーバーレスでコストを抑えながら、リッチな検索体験を提供する選択肢として検討してみてください。

参考リンク