必ず見つかるSpotlightにしておく
OSX 10.8 Mountain Lionでは、Spotlightで見つけられないキーワードがあるらしい。具体的には、「じじい」とか「シンガポール」とか「ノーライフキング」など。
- 【Mac初心者は注意!】Macではファイル名検索でヒットしない文字列がある!! - すりゴマ・ドットコム
- MacのSpotlightで検索できないファイルも簡単に検索できるEasyFindが便利。
- Spotlight: Apple サポートコミュニティ
OSX 10.8.5 Mountain Lion の Spotlight
本当なのか?実験してみた。
- ターミナルで、以下のファイルを作成してみた。(コピー&ペーストで実行)
cd ~/Documents > じじい.txt > シンガポール.txt > ノーライフキング.txt
- テキストエディット.appで、以下の新規テキスト書類(ファイル名:spotlight_test.txt)を作ってみた。
- 同様にメモ.appで、以下の新規メモを作ってみた。
スポットライトのテスト じじい シンガポール ノーライフキング
- Spotlightで検索してみると...
現実の結果 | 期待する結果 |
---|---|
NG: じじい.txt NG: spotlight_test.txt NG: スポットライトのテスト |
|
NG: シンガポール.txt NG: spotlight_test.txt NG: スポットライトのテスト |
|
OK: ノーライフキング.txt NG: spotlight_test.txt NG: スポットライトのテスト |
や、や、や!
- ことごとく検索をミスっている。唯一「ノーライフキング.txt」というファイル名のみはヒットしているが、それ以外はすべてノーマッチ。
- 「じじい」と「ノーライフキング」については、Webやメールの履歴がヒットしているが、「シンガポール」では完全なノーマッチ。
これは由々しき問題である!
- 本来ヒットするはずの項目が、検索結果に上がって来ない...。
- これほど明白な検索漏れがあると、Spotlightの信頼性が、がた落ちである。
- SpotlightとTimeMachineの存在こそが、NeXTに始まるOSXが目指したOSの完成形だったはずなのに。
- 検索漏れのある検索エンジンなんて、計算間違いするCPUみたいなもの。
何とかしなければならない!
OSX 10.6.8 Snow Leopard の Spotlight
- ちなみに、Spotlightの名誉のために、OSX 10.6.8 Snow Leopardで全く同じ条件で検索してみると...
現実の結果 | 期待する結果 |
---|---|
OK: じじい.txt OK: spotlight_test.txt OK: スポットライトのテスト |
|
OK: シンガポール.txt OK: spotlight_test.txt OK: スポットライトのテスト |
|
OK: ノーライフキング.txt OK: spotlight_test.txt OK: スポットライトのテスト |
- すべての検索で期待どおりにヒットした。本来のSpotlight性能は、こうゆうものだと思っている。
OSX 10.8 Mountain LionのSpotlightで、いったい何が起こっているのだろう?
消極的な回避策
以下の方法を覚えておけば、ある程度はまともに検索できるのだけど、あまり良い方法とは言えない...。
そもそもSpotlightとは、こんな小手先の検索技なんて知らなくても、思い浮かんだキーワードで素早く検索できることが売りのはず。また検索するなら、短いキーワードで広範囲にヒットして、そこから条件を追加して絞り込む方が使いやすい。少なくとも、Snow LeopardまでのSpotlightはそのような仕様になっていたはず。
- ファイル名を検索する時は、「ファイル名:」あるいは「filename:」を付加して検索すると、ちゃんと見つかる。
ファイル名:シンガポール
filename:シンガポール
- Finderのファイル検索でも、「ファイル名:」あるいは「filename:」を付加して検索すると、ちゃんと見つかる。
- あるいは、検索条件で「ファイル名」を選択して、その右側に「検索テキスト」を入力すると、ちゃんと見つかる。
- うっかり「ファイル名 名前が一致:」を選択してしまうと、何もヒットしない...。(誘導されやすいので気をつける)
- Finderのファイル検索と、control-option-スペースのSpotlightのウィンドウ検索は、同じではない。
- この違いを忘れて検索してしまうと、本来見つかるものも、見つからなくなってしまう...。
ファイル名の検索については上記対応でどうにかなるが、テキストファイルの内容検索については無理...。さらに探求する必要がある。
検索インデックスの検証
- Snow LeopardとMountain Lionで、検索インデックスに違いがあるのか?検証してみた。
- 空の外付けハードディスクを、まずSnow Leopardに接続する。
- 上記実験で使った「spotlight_test.txt」をコピーした。
- Snow LeopardのSpotlightで「スポットライトのテスト」を検索してみると、ちゃんと「spotlight_test.txt」がヒットした。
- 次に、同じ外付けハードディスクをMountain Lionに接続するのだけど、
- その前に以下のコマンドを実行して、検索インデックスの作成を一時停止しておく。
$ mdutil -i off
- 外付けハードディスクを接続したら、Spotlightで「スポットライトのテスト」を検索してみる。何もヒットしない...。
- 検索インデックスの作成をチェックするため、以下のコマンドを実行中にしておく。
$ sudo opensnoop -n mdworker Password: UID PID COMM FD PATH
- この状態で、検索インデックスの作成を再開する。
$ mdutil -i on
- すると、先ほどの監視コマンドの出力に、テストファイルが見えた!
$ sudo opensnoop -n mdworker Password: UID PID COMM FD PATH ...中略... 89 15504 mdworker 4 /Volumes/名称未設定 10/spotlight_test.txt 89 15504 mdworker 5 /Volumes/名称未設定 10/ノーライフキング.txt 89 15504 mdworker 5 /Volumes/名称未設定 10/シンガポール.txt 89 15504 mdworker 5 /Volumes/名称未設定 10/じじい.txt 89 15504 mdworker -1 /Volumes/.DS_Store 89 15504 mdworker 4 /Volumes/名称未設定 10/.DS_Store
- その後再び、Spotlightで「スポットライトのテスト」を検索してみる。今度は「spotlight_test.txt」がヒットした!
つまり、Mountain Lionは、Snow Leopardの検索インデックスをそのまま使っていない!
- 上書きしてるか、別の検索インデックスを作っているはずである。
- Spotlightの検索インデックスは、各ボリュームのルート直下の .Spotlight-V100 に格納されている。
- そう思って、ls コマンドで探索してみると、二つの検索インデックス(と思われるファイル構造)を発見した!
- こちらはSnow Leopardの検索インデックス。
$ ls -al /Volumes/名称未設定\ 10/.Spotlight-V100/Store-V1/Stores/D47DAAC7-5E5F-458E-B187-00E6348D1DB9 total 1560 drwx------ 61 bebe staff - 2074 10 23 15:44 ./ drwx------ 3 bebe staff - 102 10 21 10:03 ../ -rw------- 1 bebe staff - 53248 10 23 15:44 .store.db -rw------- 1 bebe staff - 68484 10 21 10:03 0.indexArrays -rw------- 1 bebe staff - 8 10 21 10:03 0.indexCompactDirectory -rw------- 1 bebe staff - 2056 10 21 10:03 0.indexDirectory -rw------- 1 bebe staff - 2731 10 21 10:14 0.indexGroups -rw------- 1 bebe staff - 4096 10 21 10:14 0.indexHead -rw------- 1 bebe staff - 32768 10 21 10:03 0.indexIds -rw------- 1 bebe staff - 20 10 21 10:03 0.indexPositions -rw------- 1 bebe staff - 752 10 21 10:03 0.indexPostings -rw------- 1 bebe staff - 4 10 21 10:14 0.shadowIndexGroups -rw------- 1 bebe staff - 4096 10 21 10:14 0.shadowIndexHead -rw------- 1 bebe staff - 28 10 23 15:44 indexState -rw------- 1 bebe staff - 0 10 21 10:03 journalExclusion -rw------- 1 bebe staff - 0 10 23 15:42 journalLive -rw------- 1 bebe staff - 0 10 23 15:42 journalSync -rw------- 1 bebe staff - 68600 10 23 15:42 live.0.indexArrays -rw------- 1 bebe staff - 8 10 23 15:42 live.0.indexCompactDirectory -rw------- 1 bebe staff - 2056 10 23 15:42 live.0.indexDirectory -rw------- 1 bebe staff - 6 10 23 15:42 live.0.indexGroups -rw------- 1 bebe staff - 4096 10 23 15:42 live.0.indexHead -rw------- 1 bebe staff - 64 10 23 15:42 live.0.indexIds -rw------- 1 bebe staff - 18 10 23 15:42 live.0.indexPositions -rw------- 1 bebe staff - 813 10 23 15:42 live.0.indexPostings -rw------- 1 bebe staff - 10 10 23 15:42 live.0.indexUpdates -rw------- 1 bebe staff - 6 10 23 15:42 live.0.shadowIndexGroups -rw------- 1 bebe staff - 4096 10 23 15:42 live.0.shadowIndexHead -rw------- 1 bebe staff - 66588 10 23 15:42 live.1.indexArrays -rw------- 1 bebe staff - 8 10 23 15:42 live.1.indexCompactDirectory -rw------- 1 bebe staff - 2056 10 23 15:42 live.1.indexDirectory -rw------- 1 bebe staff - 2731 10 23 15:42 live.1.indexGroups -rw------- 1 bebe staff - 4096 10 23 15:42 live.1.indexHead -rw------- 1 bebe staff - 32768 10 23 15:42 live.1.indexIds -rw------- 1 bebe staff - 4 10 23 15:42 live.1.indexPositions -rw------- 1 bebe staff - 148 10 23 15:42 live.1.indexPostings -rw------- 1 bebe staff - 2 10 23 15:42 live.1.shadowIndexGroups -rw------- 1 bebe staff - 4096 10 23 15:42 live.1.shadowIndexHead -rw------- 1 bebe staff - 65536 10 23 15:42 live.2.indexArrays -rw------- 1 bebe staff - 1024 10 23 15:42 live.2.indexCompactDirectory -rw------- 1 bebe staff - 8224 10 23 15:42 live.2.indexDirectory -rw------- 1 bebe staff - 2731 10 23 15:42 live.2.indexGroups -rw------- 1 bebe staff - 4096 10 23 15:42 live.2.indexHead -rw------- 1 bebe staff - 32768 10 23 15:42 live.2.indexIds -rw------- 1 bebe staff - 8192 10 23 15:42 live.2.indexPositionTable -rw------- 1 bebe staff - 4096 10 23 15:42 live.2.indexPositions -rw------- 1 bebe staff - 4096 10 23 15:42 live.2.indexPostings -rw------- 1 bebe staff - 8192 10 23 15:42 live.2.indexTermIds -rw------- 1 bebe staff - 0 10 23 15:42 live.2.indexUpdates -rw------- 1 bebe staff - 65536 10 23 15:42 live.2.shadowIndexArrays -rw------- 1 bebe staff - 8 10 23 15:42 live.2.shadowIndexCompactDirectory -rw------- 1 bebe staff - 2056 10 23 15:42 live.2.shadowIndexDirectory -rw------- 1 bebe staff - 1 10 23 15:42 live.2.shadowIndexGroups -rw------- 1 bebe staff - 4096 10 23 15:42 live.2.shadowIndexHead -rw------- 1 bebe staff - 0 10 23 15:42 live.2.shadowIndexPositionTable -rw------- 1 bebe staff - 0 10 23 15:42 live.2.shadowIndexTermIds -rw------- 1 bebe staff - 65620 10 23 15:44 permStore -rw-r--r-- 1 bebe staff - 4 10 23 15:44 shutdown_time -rw------- 1 bebe staff - 53248 10 23 15:42 store.db -rw------- 1 bebe staff - 6 10 23 15:44 store.updates -rw------- 1 bebe staff - 0 10 21 10:03 tmp.SnowLeopard
- こちらがMountain Lionの検索インデックス。
$ ls -al /Volumes/名称未設定\ 10/.Spotlight-V100/Store-V2/843F7ECA-ADE0-4063-ADF9-09412B12BDDE/ total 1624 drwx------ 58 bebe staff - 1972 10 23 15:45 ./ drwx------ 3 bebe staff - 102 10 23 15:36 ../ -rw------- 1 bebe staff - 118784 10 23 15:42 .store.db -rw------- 1 bebe staff - 65536 10 23 15:45 0.directoryStoreFile -rw------- 1 bebe staff - 1088 10 23 15:36 0.directoryStoreFile.shadow -rw------- 1 bebe staff - 1792 10 23 15:36 0.indexArrays -rw------- 1 bebe staff - 8 10 23 15:36 0.indexCompactDirectory -rw------- 1 bebe staff - 2056 10 23 15:36 0.indexDirectory -rw------- 1 bebe staff - 3277 10 23 15:36 0.indexGroups -rw------- 1 bebe staff - 4096 10 23 15:36 0.indexHead -rw------- 1 bebe staff - 48 10 23 15:36 0.indexIds -rw------- 1 bebe staff - 976 10 23 15:36 0.indexPositionTable -rw------- 1 bebe staff - 4096 10 23 15:36 0.indexPositions -rw------- 1 bebe staff - 4096 10 23 15:36 0.indexPostings -rw------- 1 bebe staff - 976 10 23 15:36 0.indexTermIds -rw------- 1 bebe staff - 14 10 23 15:36 0.indexUpdates -rw------- 1 bebe staff - 5 10 23 15:36 0.shadowIndexGroups -rw------- 1 bebe staff - 4096 10 23 15:36 0.shadowIndexHead -rw------- 1 bebe staff - 0 10 23 15:36 Lion.created -rw------- 1 bebe staff - 0 10 23 15:45 Lion.modified -rw------- 1 bebe staff - 28 10 23 15:36 indexState -rw------- 1 bebe staff - 0 10 23 15:45 journalAttr.2 -rw------- 1 bebe staff - 0 10 23 15:36 journalExclusion drwx------ 2 bebe staff - 68 10 23 15:36 journals.live/ drwx------ 2 bebe staff - 68 10 23 15:45 journals.repair/ drwx------ 3 bebe staff - 102 10 23 15:36 journals.scan/ -rw------- 1 bebe staff - 65536 10 23 15:45 live.0.directoryStoreFile -rw------- 1 bebe staff - 1088 10 23 15:36 live.0.directoryStoreFile.shadow -rw------- 1 bebe staff - 65536 10 23 15:45 live.0.indexArrays -rw------- 1 bebe staff - 1024 10 23 15:45 live.0.indexCompactDirectory -rw------- 1 bebe staff - 8224 10 23 15:45 live.0.indexDirectory -rw------- 1 bebe staff - 3277 10 23 15:45 live.0.indexGroups -rw------- 1 bebe staff - 4096 10 23 15:36 live.0.indexHead -rw------- 1 bebe staff - 32768 10 23 15:45 live.0.indexIds -rw------- 1 bebe staff - 8192 10 23 15:45 live.0.indexPositionTable -rw------- 1 bebe staff - 4096 10 23 15:45 live.0.indexPositions -rw------- 1 bebe staff - 4096 10 23 15:45 live.0.indexPostings -rw------- 1 bebe staff - 8192 10 23 15:45 live.0.indexTermIds -rw------- 1 bebe staff - 0 10 23 15:36 live.0.indexUpdates -rw------- 1 bebe staff - 65536 10 23 15:36 live.0.shadowIndexArrays -rw------- 1 bebe staff - 8 10 23 15:36 live.0.shadowIndexCompactDirectory -rw------- 1 bebe staff - 2056 10 23 15:36 live.0.shadowIndexDirectory -rw------- 1 bebe staff - 1 10 23 15:36 live.0.shadowIndexGroups -rw------- 1 bebe staff - 4096 10 23 15:36 live.0.shadowIndexHead -rw------- 1 bebe staff - 0 10 23 15:36 live.0.shadowIndexPositionTable -rw------- 1 bebe staff - 0 10 23 15:36 live.0.shadowIndexTermIds -rw------- 1 bebe staff - 65621 10 23 15:45 permStore -rw------- 1 bebe staff - 65536 10 23 15:45 reverseDirectoryStore -rw------- 1 bebe staff - 3136 10 23 15:36 reverseDirectoryStore.shadow -rw------- 1 bebe staff - 2 10 23 15:42 reverseStore.updates -rw-r--r-- 1 bebe staff - 4 10 23 15:42 shutdown_time -rw------- 1 bebe staff - 118784 10 23 15:36 store.db -rw------- 1 bebe staff - 8 10 23 15:42 store.updates -rw-r--r-- 1 bebe staff - 4 10 23 15:36 store_generation -rw------- 1 bebe staff - 0 10 23 15:36 tmp.Lion -rw------- 1 bebe staff - 0 10 23 15:36 tmp.SnowLeopard -rw------- 1 bebe staff - 6608 10 23 15:36 tmp.spotlight.loc -rw------- 1 bebe staff - 4096 10 23 15:42 tmp.spotlight.state
- 中身はブラックボックスだけど、修正日時のタイミングから、二つの検索インデックスが使い分けられていることは確かだと思う。
- Snow Leopardは、/.Spotlight-V100/Store-V1/...
- Mountain Lionは、/.Spotlight-V100/Store-V2/...
Mountain Lionでは、検索インデックスのバージョンがV2となり、構造が変更されているようだ。
検索クエリ(検索条件)の検証
$ man mdfind ...中略... (* = search* cdw || kMDItemTextContent = search* cdw)
- 例えば「シンガポール」を検索する場合、以下のmdfindコマンドを実行すると、Spotlightと同等になるのだ。
$ mdfind "* = 'シンガポール*'cdw || kMDItemTextContent = 'シンガポール*'cdw"
- 上記コマンドを実行しても、Spotlightと同じく、さっぱり何もヒットしない...。
- ところが、キーワードに続く cdw の d を外してみると、見事、かつてのSpotlightのようにヒットしてしまった!
- -onlyinオプションを指定して、検索範囲を~/Documentsに限定している。
$ mdfind "* = 'シンガポール*'cw || kMDItemTextContent = 'シンガポール*'"cw -onlyin ~/Documents /Users/bebe/Documents/シンガポール.txt /Users/bebe/Documents/spotlight_test.txt ...中略...
cdwの意味
- そもそも、cdwには何の意味があるのか?
c | 大文字・小文字を区別しない |
---|---|
d | アクセント記号のあり・なしを区別しない |
w | 小文字から大文字へ変化する部分も単語の区切りと見なす |
参考ページ:(感謝です!)
- Install SpotInside to MacBook - 日々の報告書
- Searchlight kMDItem kMDItemContentType kMDItemFSContentChangeDate
- File Metadata Search Programming Guide: File Metadata Query Expression Syntax
- なるほど、cdwは主にアルファベットの検索条件で役立ちそうな指定である。
- しかし、なぜd(=アクセント記号のあり・なしを区別しない)指定が日本語の検索に影響してしまうのだろう?
- 少なくとも、Snow Leopardまでは影響していない。
$ mdfind "* == 'シンガポール*'cdw || kMDItemTextContent == 'シンガポール*'cdw" -onlyin ~/Documents /Users/bebe/Documents/spotlight_test.txt /Users/bebe/Documents/シンガポール.txt ...中略...
メタデータの種類
$ mdfind "* == 'シンガポール*'cdw || kMDItemTextContent == 'シンガポール*'cdw" -onlyin ~/Documents
- 先ほどの検索クエリで、「kMDItemTextContent == 'シンガポール*'cdw」の部分は、メタデータの種類を指定している。
- kMDItemTextContentは、テキストファイルの内容である。
- テキストの内容に「シンガポール」で始まる単語が含まれていたら、ヒットするのだ。
- それでは「* == 'シンガポール*'cdw」の条件は何だろう?メタデータの「*」の部分が?
- 通常「*」はすべての文字列にマッチすることを意味する。
- 'シンガポール*'であれば、「シンガポール」で始まる単語すべてにマッチする。
- 例:「シンガポールスリング」「シンガポール航空」など
- ならばメタデータの「*」は、すべてのメタデータという意味になるのかもしれない。
- でも、すべてのメタデータを指定しておきながら、その後でkMDItemTextContentを指定するのは重複するのではないか?
$ mdfind "* == 'シンガポール*'cw" -onlyin ~/Documents /Users/bebe/Documents/シンガポール.txt
- 「kMDItemTextContent」も含めると、ちゃんとテキスト内容の「シンガポール」もヒットした。
$ mdfind "* == 'シンガポール*'cw || kMDItemTextContent == 'シンガポール*'cw" -onlyin ~/Documents /Users/bebe/Documents/spotlight_test.txt /Users/bebe/Documents/シンガポール.txt $ cat /Users/bebe/Documents/spotlight_test.txt スポットライトのテスト じじい シンガポール ノーライフキング
参考ページ:(感謝です!)
Spotlightの使い方
- 以上の検証から、検索インデックスのバージョンが変わったMountain Lionでも、検索クエリの「cdw」の部分を「cw」にすれば、かなり満足できる結果になりそうである。
- しかし、Spotlightで検索クエリを指定することなんて、果たしてできるのだろうか?
- Spotlightの基本的な使い方として「メタデータ名称:値」で検索条件を指定できることは知っている。
- メタデータ名称には、kMDItem...で始まる正式な名称以外に、短縮名称も利用できる。日本語名称も用意されている。
入力語句 | 意味 |
---|---|
Time Capsule | "Time"と"Capsule"の両方を含むファイルにマッチ。 |
"Time Capsule" | "Time Capsule"という一連の語句を含むファイルにマッチ。 |
Time -Capsule | "Time"は含むが、"Capsule"を含まないファイルにマッチ。 |
Time OR Capsule | "Time"か"Capsule"のどちらか一方を含むファイルにマッチ。 |
NOT(Time Capsule) | "Time"も"Capsule"も含まないファイルにマッチ。 |
ファイル名:シンガポール | ファイル名に"シンガポール"を含むファイルにマッチ。 |
ファイル名:シンガポール 種類:text | ファイル名に"シンガポール"を含む、テキストファイルにマッチ。 |
ファイル名:シンガポール 日付:2013/10/23 | ファイル名に"シンガポール"を含む、2013/10/23のファイルにマッチ。 |
ファイル名:シンガポール 日付:>2013/10/23 | ファイル名に"シンガポール"を含む、2013/10/23以降のファイルにマッチ。 |
ファイル名:シンガポール 日付:<2013/10/23 | ファイル名に"シンガポール"を含む、2013/10/23以前のファイルにマッチ。 |
kMDItemPixelHeight:>3000 height:>3000 高さ:>3000 |
画像の高さが3000ピクセルより大きいファイルにマッチ。 |
- 上記の方法でメタデータを指定することはできる。ところが、生成される検索クエリの詳細な指定(cdwの部分)まではできないのだ...。
独自の検索クエリーを指定する
- Spotlightの入力部分で直接的に指定できないからと言って、諦めるのはまだ早い。
- 試行錯誤しているうちに、独自の検索クエリを自由に指定する方法を思いついた。
- まず、Spotlightで「シンガポール」を検索する。
- 相変わらず何もヒットしないのだけど、気にしないで「Finderにすべてを表示します」を選択する。
- すると、何もヒットしてないウィンドウが表示されるので、検索条件を保存する。
- 「~/ライブラリ/保存済みの検索条件」を開くと、さっき保存した検索条件が見つかる。
- 選択して、command-Iで情報を見てみる。
- すると、クエリー:の項目にマニュアルどおりの条件が記載されている。
- このクエリーを修正できれば、もっと自由なSpotlight検索ができるはず。
- この検索条件をテキストエディットで開いてみる。
- すると、その中身はplistであり、以下の内容が確認できた。
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>CompatibleVersion</key> <integer>1</integer> <key>RawQuery</key> <string>(true) && (((* = "シンガポール*"cdw || kMDItemTextContent = "シンガポール*"cdw)))</string> <key>RawQueryDict</key> <dict> <key>FinderFilesOnly</key> <false/> <key>RawQuery</key> <string>(true) && (((* = "シンガポール*"cdw || kMDItemTextContent = "シンガポール*"cdw)))</string> <key>SearchScopes</key> <array> <string>kMDQueryScopeComputer</string> </array> <key>UserFilesOnly</key> <true/> </dict> ...中略...
- 先ほど見た検索クエリーも確認できる!
- すかさずcdwの部分をすべてcwに修正。
- 修正したら、忘れずに保存しておく。
- そして、Finderから修正後の検索条件をダブルクリックして開いてみると...
「ノーライフキング」問題
- 喜んだのもつかのま、「ノーライフキング」については、この検索クエリーの修正でもテキストファイルの内容がヒットしないことに気付いた。
- ファイル名はヒットした。
- なぜ「ノーライフキング」はヒットしないのか?
- 「ノーライフキング」は小説(映画にもなった)のタイトルであるが、おそらく、この文字の並びに秘密がある。
- 固有名詞なのだけど、「ノーライフキング」を知らない人が読んだら、「ノー」と「ライフ」と「キング」に分割して、必至に意味を探ろうと思うはず。
- 英語などは単語区切りが明白だが、日本語の場合は先頭から文字を読んで、意味のある語句を認識しながら読み進める。
- おそらく、Spotlightが検索インデックスを作成する時も同様に、意味のある語句で区切って、検索インデックスに登録するはずである。
- その時、「ノーライフキング」は「ノー」と「ライフ」と「キング」に分かれて登録されているのかもしれない...。
- 試しに、テキストファイルの内容を "ノーライフキング" のようにクォートしてみると、ちゃんとヒットした。
- 少なくともSnow Leopard時代のSpotlightではちゃんとヒットしていたのに、Mountain Lionの検索インデックスはクォートしないとヒットしない、残念な仕様である。
- 検索インデックスが違ってしまっているのだから、「ノーライフキング」に対しては、なす術がないのかと諦めかけていたが、一つ閃いた。
- 検索クエリーを「ノー*ライフ*キング*」のように指定してみた。
$ mdfind 'kMDItemTextContent = "ノー*ライフ*キング*"' -onlyin ~/Documents /Users/zari/Documents/spotlight_test.txtすると、見事にヒット!
スクリプトにまとめる
- 以上の技を駆使すれば、Mountain LionのSpotlightでも、かなり満足できる検索結果を得られそうだ。
- 但し、毎回手作業で検索クエリーを修正するなんて言うのは、面倒くさすぎる...。
- ここはもう、いつものAppleScriptでショートカット一発、素早く満足できる検索結果を手に入れたい。
- メニューバーのSpotlightで検索して、出力される検索結果に満足できない時に、ショートカットから素早く漏れのない検索結果を出力するのだ。
Spotlight.scpt
- ~/Library/Scripts/Spotlight.scpt として保存した。
- 雛形のSpotlight.savedSearch.plistに検索語句を代入して、保存済みの検索条件を作成している。
- このスクリプトは、メニューバーのSpotlightを利用中に、ショートカットで呼び出す必要がある。
- よって、Quicksilverなどで、事前にショートカットを割り当てておく必要がある。
- 自分の場合、control-option-スペースを割り当てた。
- OSXデフォルトのcontrol-option-スペースは無効にした。
if spotlight_running() then expanded_spotlight() else normal_spotlight() end if on expanded_spotlight() tell application "System Events" delay 0.3 keystroke "a" using {command down} delay 0.3 keystroke "c" using {command down} delay 0.3 keystroke space using {control down} end tell set key_word to the clipboard as text set ext_word to join(key_word's text items, "*") set savedSearch to (path to temporary items)'s POSIX path & "Spotlight.savedSearch" set savedSearch_plist to (path to scripts folder)'s POSIX path & "Spotlight.savedSearch.plist" do shell script "rm -f " & savedSearch do shell script "cat " & savedSearch_plist & " | sed s/__KEYWORD__/" & key_word & "/g | sed s/__CONTENT__/" & ext_word & "/g >> " & savedSearch do shell script "open " & savedSearch tell application "Finder" to activate delay 1 zoom_finder_window1() end expanded_spotlight on normal_spotlight() tell application "System Events" keystroke space using {control down} end tell end normal_spotlight --Spotlightが表示中かどうか on spotlight_running() tell application "System Events" tell process "SystemUIServer" menu bar 2's menu bar item 1's selected end tell end tell end spotlight_running --Finderのアクティブなウィンドウをズームする on zoom_finder_window1() tell application "System Events" tell process "Finder" click window 1's button 2 end tell end tell end zoom_finder_window1 on join(src_list, delimiter) set last_delimiter to AppleScript's text item delimiters set AppleScript's text item delimiters to delimiter set res to src_list as text set AppleScript's text item delimiters to last_delimiter res end join
- コーディングするにあたり、「ノーライフキング」から「ノー*ライフ*キング*」をどうやって導き出すか?という問題にぶつかった。
- 人間なら、自分の知識から簡単に「ノー*ライフ*キング*」を導ける。
- しかし、コンピュータに同じことさせるのは、非常に難しい。
- ちゃんとやるなら、MeCabなどの形態素解析エンジンを導入して、分かち書きを求める必要がある。
- しかし、そこまでやるのは面倒だし、インストールの手間もかかる...。
- 今回の目的は、正しい単語区切りをすることではなく、検索漏れのないSpotlightにすることである。
- しばし考え、それはちょっとやり過ぎ感もあると思いながら、決断した。
- よって、このスクリプトは、テキストファイル中に検索語の一文字ずつが、順に出現する内容でもヒットしてしまう...。
- 短い単語だと、この仕様によって、膨大な検索結果がヒットしてしまう。
- どうして検索語が含まれていないのにヒットするのか悩んだら、おそらくこの原因。
- しかし、長い単語なら、ほとんど気にならないレベルで正しい検索結果となりそう。
Spotlight.savedSearch.plist
- このxmlファイルは、保存済みの検索条件の雛形となる。
- ~/Library/Scripts/Spotlight.savedSearch.plist として保存しておく。
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> <plist version="1.0"> <dict> <key>CompatibleVersion</key> <integer>1</integer> <key>RawQuery</key> <!-- <string>(true) && (((* = "__KEYWORD__*"cdw || kMDItemTextContent = "__KEYWORD__*"cdw || kMDItemTextContent = "__CONTENT__*"cdw)))</string> --> <string>(true) && (((* = "__KEYWORD__*"cw || kMDItemTextContent = "__KEYWORD__*"cw || kMDItemTextContent = "__CONTENT__*"cw)))</string> <key>RawQueryDict</key> <dict> <key>FinderFilesOnly</key> <false/> <key>RawQuery</key> <!-- <string>(true) && (((* = "__KEYWORD__*"cdw || kMDItemTextContent = "__KEYWORD__*"cdw || kMDItemTextContent = "__CONTENT__*"cdw)))</string> --> <string>(true) && (((* = "__KEYWORD__*"cw || kMDItemTextContent = "__KEYWORD__*"cw || kMDItemTextContent = "__CONTENT__*"cw)))</string> <key>SearchScopes</key> <array> <string>kMDQueryScopeComputer</string> </array> <key>UserFilesOnly</key> <true/> </dict> <key>SearchCriteria</key> <dict> <key>FXCriteriaSlices</key> <array> <dict> <key>criteria</key> <array> <string>com_apple_UserSearchStringAttribute</string> <integer>104</integer> </array> <key>displayValues</key> <array> <string>Items matching text</string> <string>__KEYWORD__</string> </array> <key>rowType</key> <integer>0</integer> <key>subrows</key> <array/> </dict> </array> <key>FXScope</key> <integer>1396925814</integer> <key>FXScopeArrayOfPaths</key> <array> <string>kMDQueryScopeComputer</string> </array> </dict> <key>SuggestedAttributes</key> <array/> <!-- ==================== ここから ViewSettings ここから ==================== --> <key>ViewSettings</key> <dict> <key>ExtendedListViewSettings</key> <dict> <key>calculateAllSizes</key> <false/> <key>columns</key> <array> <dict> <key>ascending</key> <true/> <key>identifier</key> <string>name</string> <key>visible</key> <true/> <key>width</key> <integer>521</integer><!-- "名前"の列幅--> </dict> <dict> <key>ascending</key> <false/> <key>identifier</key> <string>dateModified</string> <key>visible</key> <false/> <key>width</key> <integer>181</integer> </dict> <dict> <key>ascending</key> <false/> <key>identifier</key> <string>dateCreated</string> <key>visible</key> <false/> <key>width</key> <integer>181</integer> </dict> <dict> <key>ascending</key> <false/> <key>identifier</key> <string>size</string> <key>visible</key> <false/> <key>width</key> <integer>97</integer> </dict> <dict> <key>ascending</key> <true/> <key>identifier</key> <string>kind</string> <key>visible</key> <true/> <key>width</key> <integer>156</integer><!-- "種類"の列幅--> </dict> <dict> <key>ascending</key> <true/> <key>identifier</key> <string>label</string> <key>visible</key> <false/> <key>width</key> <integer>100</integer> </dict> <dict> <key>ascending</key> <true/> <key>identifier</key> <string>version</string> <key>visible</key> <false/> <key>width</key> <integer>75</integer> </dict> <dict> <key>ascending</key> <true/> <key>identifier</key> <string>comments</string> <key>visible</key> <false/> <key>width</key> <integer>300</integer> </dict> <dict> <key>ascending</key> <false/> <key>identifier</key> <string>dateLastOpened</string> <key>visible</key> <true/> <key>width</key> <integer>141</integer><!-- "最後に開いた日"の列幅--> </dict> <dict> <key>ascending</key> <false/> <key>identifier</key> <string>dateAdded</string> <key>visible</key> <false/> <key>width</key> <integer>181</integer> </dict> </array> <key>iconSize</key> <real>16</real> <key>showIconPreview</key> <true/> <key>sortColumn</key> <string>dateLastOpened</string> <key>textSize</key> <real>12</real> <key>useRelativeDates</key> <true/> <key>viewOptionsVersion</key> <integer>1</integer> </dict> <key>ListViewSettings</key> <dict> <key>calculateAllSizes</key> <false/> <key>columns</key> <dict> <key>comments</key> <dict> <key>ascending</key> <true/> <key>index</key> <integer>7</integer> <key>visible</key> <false/> <key>width</key> <integer>300</integer> </dict> <key>dateCreated</key> <dict> <key>ascending</key> <false/> <key>index</key> <integer>2</integer> <key>visible</key> <false/> <key>width</key> <integer>181</integer> </dict> <key>dateLastOpened</key> <dict> <key>ascending</key> <false/> <key>index</key> <integer>8</integer> <key>visible</key> <true/> <key>width</key> <integer>141</integer><!-- "最後に開いた日"の列幅--> </dict> <key>dateModified</key> <dict> <key>ascending</key> <false/> <key>index</key> <integer>1</integer> <key>visible</key> <false/> <key>width</key> <integer>181</integer> </dict> <key>kind</key> <dict> <key>ascending</key> <true/> <key>index</key> <integer>4</integer> <key>visible</key> <true/> <key>width</key> <integer>156</integer><!-- "種類"の列幅--> </dict> <key>label</key> <dict> <key>ascending</key> <true/> <key>index</key> <integer>5</integer> <key>visible</key> <false/> <key>width</key> <integer>100</integer> </dict> <key>name</key> <dict> <key>ascending</key> <true/> <key>index</key> <integer>0</integer> <key>visible</key> <true/> <key>width</key> <integer>521</integer><!-- "名前"の列幅--> </dict> <key>size</key> <dict> <key>ascending</key> <false/> <key>index</key> <integer>3</integer> <key>visible</key> <false/> <key>width</key> <integer>97</integer> </dict> <key>version</key> <dict> <key>ascending</key> <true/> <key>index</key> <integer>6</integer> <key>visible</key> <false/> <key>width</key> <integer>75</integer> </dict> </dict> <key>iconSize</key> <real>16</real> <key>showIconPreview</key> <true/> <key>sortColumn</key> <string>dateLastOpened</string> <key>textSize</key> <real>12</real> <key>useRelativeDates</key> <true/> <key>viewOptionsVersion</key> <integer>1</integer> </dict> <key>WindowState</key> <dict> <key>ShowPathbar</key> <true/> <key>ShowSidebar</key> <false/> <key>ShowStatusBar</key> <true/> <key>ShowToolbar</key> <true/> <key>SidebarWidth</key> <integer>161</integer> <key>WindowBounds</key> <string>{{9999, 9999}, {834, 425}}</string><!-- ウィンドウのサイズ --> </dict> </dict> <!-- ==================== ここまで ViewSettings ここまで ==================== --> </dict> </plist>以上で、control-option-スペースで素早くSnow Leopard時代の検索性能を手に入れられる!
- 奇しくも、この日記を書いている途中で、OS X 10.9 Mavericksの配布が開始された。
- MavericksのSpotlightは、果たして「じじい」「シンガポール」「ノーライフキング」を漏れなく検索できるのか?