適当な翻訳シリーズ[2] Working with Spotlight

http://developer.apple.com/macosx/spotlight.html

Spotlightは、オペレーティングシステムの歴史の中で画期的な機能です。長年人々は、ウェブと同じくらい高速でカンタンに検索でき、より正確な検索を実現するためにメタデータを用いるファイルシステムを作ることについて語ってきました。他のOSはもう長い間この機能の適用を約束しています(訳注:LonghornWinFSだな)。また、サードパーティプラグインはこの機能を提供しはじめています(訳注:もちろんGoogle Desktop Searchのことだ)。しかし、Tigerは完全にOSに統合された、システム上の全てのファイルにわたって効率的な検索を提供する、最初のメジャーなOSです。

誤りを排すため、Spotlightはシステムに「継ぎ足した機能」ようなものではありません。Spotlightは、ファイルシステムというOSの基礎部分にしっかりと統合された、完全に新しい検索技術なのです。ファイルが作成/保存/移動/コピー/削除される毎に、ファイルシステムは自動的に索引付けし、カタログ化し、全てのバックグラウンドで実行されているかもしれないどのような問い合わせも確実に準備します。これらの機能は、既に印象的な能力を持っているジャーナル機能が有効化されたHFS+ファイルシステムの上に作成されています。

Spotlightはエンドユーザに機能をもたらすだけでなく、開発者にもさまざまな検索技術をもたらします。アプリケーションがファイルを見つけるための検索技術、情報をSpotlightにロードするためのプラグイン、データをアプリケーションの中でカンタンに扱うことができます。制限も限界もありません。

Spotlightの技術は以下のようなものです:

これらは、独立した技術の集合というだけでなく、互いに協調して動作し、SpotlightはアプリケーションがOSにプラグを差し込んで完全に新しい形でファイルを扱うことを可能にします。たとえば、資産管理アプリケーションを作成している場合、Finderをつかって人間が苦労してファイルを見つけ出そうと苦労するのではなく、検索条件にあったファイルを見つけ出すとためにSpotlightを使うことが出来るでしょう。また、アプリケーションが様々な種類のワークフローをサポートすることを特徴付けられているのであれば、特定のキーワードでマークされる必要のあるファイルの全てを見つけるためにSpotlightを使うことが出来るでしょう。いったんSpotlightを使い始めたらもう後戻りは出来ないでしょう。

この文書では、Spotlightがどんなふうに働くか、どのようにSpotlight Storeにプログラムから問い合わせを発行するか、ししてどのようにカスタムのimporter plug-inを作るかについて解説します。カバーすべき部分はかなり広範囲ですが、まず最初に、メタデータは何かについて定義することからはじめましょう。

メタデータとは何か

メタデータとは、要は、データについてのデータです。高さ、幅、サイズ、作成者、著作権者、タイトル、編集者、作成日時、最終更新日時などの、ファイルが持っているデータについての記述です。実際、メタデータだと考えられるデータは非常に大量にあるので、キーによって索引付けられた辞書の形に一般化されています。これにより、データについての「いつ、誰が、何を」を考えることが可能になるのです。

更新日時や所有者、アクセス権限といったいくつかの種類のメタデータは、ファイルシステムによってファイルの外側に保持されており、様々なメカニズムによってアクセスが可能です。しかし、殆どの興味深い種類のメタデータは、ファイルの中にあります。たとえば、デジタルカメラは、露出の情報やフラッシュが使用されたかどうかなど、全ての種類のメタデータを作成するイメージファイルに保存します。また、Adobe PhotoshopMicrosoft Wordといった殆どのアプリケーションが作成するファイルは、かなり多くのメタデータを保存しています。

いままで、これらのデータはファイルの中に埋め込まれていて、これらの検索をすることは大変なことでした。Spotlightは、高速でカンタンで効率的な検索を可能にするために、これらの情報をSpotlight Storeの中に収拾します。

The Spotlight Store

Spotlight Storeは、ファイルシステムレベルのデータベースです。DBは、索引やファイルについての全てのメタデータ属性を、ファイルシステムの上に保持します。ファイルが作成、コピー、更新、削除される毎に、Spotlightはコンテンツの索引とメタデータストアの両方に情報を更新します。

http://developer.apple.com/macosx/images/server-diagram.jpg

コンテンツの索引は、MacOSX 10.3 Pantherで導入されたSearch Kitのより進化し最適化されたバージョンを使用し作られています。最適化によって、ちょっとだけ速くなったというわけではありません。TigerのSearch kitは、Pantherに比べ、索引作成が3倍高速になり、インクリメンタル検索では最大20倍も高速化されています。

一方、メタデータストアは、メタデータ特有のニーズを扱うために設計された、完全に新しいデータベースです。内部的には、MDItemオブジェクトによって表現されます。それぞれのMDItemは、ユニークなキー値によって組織化された様々なメタデータ属性の辞書を含んでいます。これらのキーは、以下のテーブルにリストアップされています。

→このへんのこと
http://developer.apple.com/documentation/Carbon/Reference/MDItemRef/index.html

これらのキー値は、特定のフォーマット中の名前というよりも抽象的な存在であることに注意して下さい。これは、異なるファイルフォーマットで同じメタデータが異なった意味で使われるからです。メタデータの用語を単一のネームスペースに正規化することは、制約された検索をより簡素化します。Tigerは様々なメタデータ型を取り扱うために大量のキーを定義して出荷されます。(訳注:実際、MDItemRefでは極めて多種多様なメタデータ型が定義されており、よほどのことが無い限り、新たなキーを定義する必要はない。もちろん、新しく定義することもできるようだ)

Spotlight Storeについて、もう一点注意があります。ファイルシステム毎に一つのコンテンツ索引と一つのメタデータ保管庫があります。従って、ファイルシステムに属するファイルと共にコンテンツ索引とメタデータが維持されます。これは、Mac間で外部Firewireドライブを付け替えたりするときに重要です。

以上で、Stotlightがファイルのメタデータと索引をどのように保管するかが分かりました。以下では、どうやってプログラミングするのかを説明します。

ファイルのメタデータを調べる

ファイルのメタデータを見るための最も簡単な方法は、ファイルのパスを使ってMDItemオブジェクトを作ることです。CoreServicesフレームワークをつかってこれを実行するためには、以下のようなコードを使います。

CFStringRef path = CFSTR("/Users/erika/Pictures/vacation.jpg");
MDItemRef item = MDItemCreate(kCFAllocatorDefault, path);

属性値のリストを取得するためには:

CFArrayRef attributeNames = MDItemCopyAttributeNames(item);

その後、特定の属性を得るためには:

CFTypeRef ref = MDItemCopyAttribute(item, attributeName);

以上のように、MDItemはファイルのメタデータ属性に関する単純なラッパーで、どの値へのアクセス方法もだいたい同じです。しかし、これがSpotlightにアクセスするための全ての方法だというのであれば、これ以上これについて話す必要はないでしょう。Spotlightの魔法は、条件にあった全てのファイルをSpotlightに問い合わせすることができるという点にあります。

問い合わせを作る

問い合わせを作成するし、それらの問い合わせからの応答の中にあるファイルリストを得る能力は、Spotlightがファイルシステムの典型的な働きを超え、完全に新しいカテゴリのアプリケーションを作ることを可能にします。

問い合わせを作成するとき、以下の3つの方法が使えます:

問い合わせは、Cライクな式のような単純な言語を使って作成されます。たとえば、"Tiger"というキーワードを持っている全てのファイルを検索するためには、こうなります:

kMDItemKeywords == "*Tiger*"

プログラムの中では、CoreServicesフレームワークをここでも使います。この問い合わせは、以下のようなコードで作成されます;

MDQueryRef query;
query = MDQueryCreate(kCFAllocatorDefault,
CFSTR("kMDItemKeywords == '*Tiger*'"),
NULL,
NULL);

その後、問い合わせ実行を開始します。

MDQueryExecute(query, kMDQueryWantsUpdates);
Once the query has been run, you can read the results:

CFIndex count = MDQueryGetResultCount(query);
for (i = 0; i < count; i++) {
MDItemRef item = MDQueryGetResultAtIndex(query, i);
}

問い合わせは(上述の)ワンショットモードで実行するか、ループの中で動作する動的問い合わせとして実行できます。動的問い合わせは、継続的にファイルシステムを監視する必要がある時に結うようです。問い合わせにマッチするファイルがセーブされるときに、問い合わせに新しい情報が影響することを許容すると、アプリケーションのコードが呼び出されます。

既に非常に単純な問い合わせは示しましたので、作成することができる問い合わせのアイディアを提示するために、以下のようなもっと複雑な問い合わせを示します:

((kMDItemTextContent = "Tiger*"cd)) &&
(kMDItemLastUsedDate >= $time.yesterday) &&
(kMDItemContentType != com.apple.email.emlx) &&
(kMDItemContentType != public.vcard)

この問い合わせは、内容に"Tiger"というコトバが入っており、かつ、昨日以降に使用され、かつ、アドレスブックでもE-Mailでもない内容にマッチします。そして、このような複雑な問い合わせでも十分でない場合は、組み分けや並び替えを使うことができます。

複雑な問い合わせを見つけるための最良の手段の一つは、Finderを使うことです。Finder画面の右上にある検索ボックスで問い合わせを作成して保存してください。そうすると、ホームフォルダ(訳注:のライブラリの中)の「保存済みの検索条件」を見れば、保存された検索フォルダをスマートフォルダとして見ることができるでしょう。このスマートフォルダの「情報を見る」を選ぶと、先ほど実行した問い合わせの式を見ることが出来ます。

http://developer.apple.com/macosx/images/saved-search.jpg

Meta-Data Importers

Tiger出荷時は、iTunesやAddressBookといったAppleのアプリケーションによって使われる全ての重要なファイルフォーマットと同じくらい、様々な一般的ファイルフォーマットに対してもImporterが付属します。

もしアプリケーションが独自のファイルフォーマットを使っていたり、サポートされていないファイルフォーマットだったりした場合は、Spotlightは、それらの情報を理解するためにちょっとした助けが必要です。Spotlightにこの助けを与えるためには、そのファイルフォーマットの入出力方法を理解しているアプリケーションを、meta-data importerプラグインに提供することができます。

meta-data importerプラグインを作るには3つの基本的な手順が必要です。

  • GUUID(globally unique universal id)の定義
  • プラグインのために新しいXcodeプロジェクトを作成し、info.plistの設定をする
  • GetMetadataForFile()メソッドを実装する

GUUIDの定義

GUUIDは、128バイトの、ユニークであることが保証された値です。Spotlightは、様々なmeta-data importerプラグインを識別するためにこれを使います。GUUIDを定義するためには、uuidgenコマンドをコマンドラインで実行して下さい。

$ uuidgen
09B33E82-226B-11D9-9B1C-000D932ED97A

新しいXcodeプロジェクトの作成

New Projectダイアログの"Standard Apple Plugins"の中に"Metadata Importer"プロジェクトがあります。

http://developer.apple.com/macosx/images/new-project.jpg

作成後、info.plistを以下のように編集する必要があります。

  • CFPlugInFactoriesキーに先ほど取得したGUUIDを設定して下さい。
  • CFPlugInTypesキーにもGUUIDを設定して下さい。
  • プラグインがLSItemContentTypeキーで処理するUTIファイルタイプを特定して下さい。

次に、コード中にGUUIDを定義するのは以下です。

#define PLUGIN_ID "09B33E82-226B-11D9-9B1C-000D932ED97A"

GetMetadataForFileメソッドの実装

最後のステップは、実際にコードを書くことです。メソッドのプロトタイプは以下です:

Boolean GetMetadataForFile(void *thisInterface,
CFMutableDictionaryRef attributes,
CFStringRef contentTypeUTI,
CFStringRef path)
{
/* do the actual work of pulling meta data from the file */
return TRUE;
}

このメソッドでは、与えられたパスのファイルを開き、そこからメタデータを取得しなければなりません。次に、メタデータ属性値とキーを与えられた属性ディクショナリに設定します。最後に、成功した場合はTRUEを、データを返さない場合はFALSEを返します。

プラグインが作成されテストが終わったら、以下のどこかのディレクトリに配置することで、Spotlightから利用が可能になります。

~/Library/MDImporters
/Library/MDImporters

プラグインを可能な限り最適化することは重要です。結局のところ、プラグインは、プラグインが扱う種類のファイルが作成/更新/削除されるときは常に実行されるからです。必ず、CPU/メモリの双方を適切に使用するように心がけてください。

Cocoa APIのサポート

Tigerは、NSMetadataItem,NSMetadataQuery,NSMetadataResultGroup、そしてNSPredicateクラスを使うことでSpotlightのフルサポートを提供しています。CocoaAPIは、既にこの文書中で紹介したCoreServicesAPIと同等の機能をサポートしています。
また、Cocoa Metadata APIは、Key-value coding/observingに完全に互換性があります。従って、Cocoa Bindingに関するAPIを使うことができます。

meta-data plug-in APIについても同じように、Cocoaベースのファイルハンドリングコードを使えます。.cを.mに変えて、Foundation Frameworkをインポートしてリンクすればよいのです。

コマンドラインとの統合

Spotlightについて、もう一点言及すべき点があります。Spotlightのコア機能はOSの非常に基本的なレベルで動作しているので、上級者にとっては、メタデータを確認したり問い合わせを実行するのにコマンドラインツールを使うことは自然です。

最初のコマンドはmdlsです。ディレクトリにある全てのファイルを表示する伝統的なUnixのlsコマンドと同様に、mdlsはファイルの持っている全てのメタデータ属性を表示します。以下が実行例です;

$ mdls metadata.jpg
kMDItemAttributeChangeDate = 2004-10-20 01:00:15 -0700
kMDItemBitsPerSample = 24
kMDItemColorSpace = "RGB "
kMDItemContentType = "public.jpeg"
kMDItemContentTypeTree = ("public.jpeg",
"public.image",
"public.data",
"public.item",
"public.content")
kMDItemDisplayName = "metadata.jpg"
kMDItemFSContentChangeDate = 2004-10-19 00:13:04 -0700
kMDItemFSCreationDate = 2004-10-19 00:13:04 -0700
kMDItemFSCreatorCode = 0
kMDItemFSFinderFlags = 0
kMDItemFSInvisible = 0
kMDItemFSLabel = 0
kMDItemFSName = "metadata.jpg"
kMDItemFSNodeCount = 0
kMDItemFSOwnerGroupID = 501
kMDItemFSOwnerUserID = 501
kMDItemFSSize = 21917
kMDItemFSTypeCode = 0
kMDItemID = 246476
kMDItemKind = "JPEG Image"
kMDItemLastUsedDate = 2004-10-19 00:13:04 -0700
kMDItemPixelHeight = 213
kMDItemPixelWidth = 624
kMDItemResolutionHeightDPI = 72
kMDItemResolutionWidthDPI = 72
kMDItemUsedDates = (2004-10-19 00:13:04 -0700)

mdfindツールを使うことで以下のように問い合わせを実行することもできます:

$ mdfind "kMDItemAcquisitionModel == 'Canon PowerShot S45'"
/Users/erika/Documents/vacation1.jpg
/Users/erika/Documents/vacation2.jpg
/Users/erika/Documents/vacation3.jpg

コマンドラインツールは、上級者にとって有用なだけでなく、シェルスクリプトでも使えます。たとえば、"Tiger"というキーワードを持つファイルをバックアップするシェルスクリプトは、以下の通りです:

for i in `mdfind Tiger`
do
cp $i /Volumes/Backup/$i
done

結論

以上のように、Spotlightはまさしく、スクリーンの右上にある可愛らしい検索ボックスというだけではなく、はるかに様々なものをもたらしてくれます。そして、それはFinderの提供する新しい高度な検索機能よりも更に多機能なのです。それは、ファイルを扱う完全に新しい方法です。そして、Appleオペレーティングシステムに内蔵されたこういった種類の機能を最初に提供します。更に、これらの機能を使いやすいAPIを通してアプリケーションから使うことが可能です。Spotlightは、高速で効率的であり、アプリケーションに対する使い勝手を永遠に変えてしまうでしょう。