1. はじめに
背景
皆さんはアプリ開発や研究でファイルを扱うことはありますか?
GitHubなどを見ていると,
- src
- doc
- tests
のように,きちんとディレクトリを分けて管理している構成をよく見かけます.ルールやテンプレートがあるのかもしれませんが,正直に言うと──
めんどくさい!!
そう思ったことはないでしょうか(ないか..).
一方で,ファイルをディレクトリで整理するのではなく,ファイルそのものを意味で扱うという発想も過去には存在しました.現代人も悩むファイル管理のあり方に対して,30年以上前にすでに別の答えを提示しようとしていたのです.
これは単なる変わり者のアイデアではなく,当時としては先進的すぎたために主流にならなかった技術とも言えるかもしれません.
本記事で,それはどんな思想なのか,現在なら通用するのかを再評価してみました.
本記事の内容は,検索に関する研究を行っている過程で調べたことです.
ファイル管理の問題
従来の階層型ファイルシステム(ディレクトリ構造)では,人間の多面的な分類を表現しきれません.
例えば,”研究発表資料.pptx”というファイルがあった場合,人間はこのファイルを
- 「研究に関する資料」
- 「発表で使う資料」
- 「暗号に関する研究資料」
- 「PowerPointファイル」
という複数の観点から認識します.
しかし階層型ファイルシステムでは,ファイルは通常一箇所に配置される(ハードリンクやシンボリックリンクを除く)ため,
/home/user/2026/0529/
のようなディレクトリに保存されることとなり得ます.
すると,「あー,暗号研究のあの資料どこにあったっけー?」という人間の認識と実際の保存場所が一致しないことがありえます.
「いやいや,そんなもんエクスプローラーの検索バーにキーワード入れればいいっしょ?」という意見もあるだろうと思います.
実際,現代のOSではファイル名や内容,メタデータなどを対象とした検索機能が提供されており,ユーザはディレクトリ構造を意識せずファイルへ到達できる場合も多いです.
こうした検索はファイルシステムとは独立した仕組みで提供されているものである一方で,検索によってファイルへアクセスするという考え方そのものをファイルシステムへ取り込もうとした研究,
Semantic FileSystem
がかつて提案されました.
2. Semantic FileSystem(SFS) とは
ファイルの保存場所ではなく,「意味」に基づいてファイルへアクセスすることを目指したファイルシステムで,1991年に発表された研究内容です.
提案背景
Semantic FileSystemが提案された背景には,従来の階層型ファイルシステムにおける検索の難しさがありました.
また,当時の計算機環境では,現在のようにファイルシステム全体を高速に検索することも容易ではありませんでした.
そこでSFSでは,ファイルが持つ属性をデータベースとして管理し,それらを検索することで目的のファイルへ到達する仕組みが提案されました.
具体的には
従来のファイルシステムでは,
/home/user/2026/0529/研究発表資料.pptx
のようにファイルの「位置」によって管理します.
一方,SFSではファイルから「属性(attribute)」を抽出し,検索に利用します.
例えば,
研究発表資料.pptx
type=pptx
topic=cryptography
usage=presentation
year=2026
のような属性を持つと考えます.
ユーザはこれらの属性を用いてファイルを検索できます.
仮想ディレクトリ
SFSでは検索結果を"ディレクトリ"として見せることが出来ます.
例えば,/type/pptx/cryptography/presentation/2026/研究発表資料.pptx
というディレクトリは,実際には
type=pptx
AND
topic=cryptography
AND
usage=presentation
AND
year=2026
という検索条件を表しています.
ユーザにとってはディレクトリを開いているつもりでも,内部では検索条件(検索クエリ)を組み立てています.逆をたどれば,検索条件をディレクトリとして並べていくと,ファイルにありつけるということですね.
データベース理論では,このような条件の組み合わせを論理積クエリと呼びます
3. 実例
とりあえず手軽にSFSを知るために,実際に使いながら見てみましょう.
① TMSU
(SFSというよりは,タグベース仮想ファイルシステム)
ポイント
- SQLiteとFUSEを用いて実装されている
- タグをユーザが登録する
$ tmsu tag banana.txt fruit art year=2015 # タグ登録
tmsu: new tag 'fruit'
tmsu: new tag 'art'
tmsu: new tag 'year'
tmsu: new value '2015'
tmsu: 'banana.txt' is a duplicate
$ tmsu tag mikan.bat fruit winter year=1615
tmsu: new tag 'winter'
tmsu: new value '1615'
tmsu: 'mikan.bat' is a duplicate
$ tmsu files fruit and winter # タグで検索
./mikan.bat
$ tmsu files fruit and not winter
./banana.txt
タグをつけて,それを満たすものを検索することはもちろん,これは含んでこれは含まない(and not)みたいな方法も可能です.
具体的な処理
流れは以下のとおりです.
①ユーザがタグ条件を指定する.
② TMSUはSQLiteデータベースを検索し,条件に一致するファイル一覧を取得する.
③TMSU(FUSE経由)は検索結果を仮想的なディレクトリ構造として生成する.
④ ユーザは生成されたディレクトリ構造を辿ることで検索結果を閲覧できる.
⑤ 実際のファイル操作時には,仮想ディレクトリ内のシンボリックリンクを介して元のファイルへアクセスする.
検索をSQLiteが担当して,FUSEは各ファイルをディレクトリ構造に落とし込む
では試しに,仮想ファイルシステムをマウントして中身を見てみましょう.filesフォルダ内にそれまでたどったフォルダ(タグ)に一致するファイルのシンボリックリンクが置いてあります.なるほど,検索とディレクトリの対応を見せるためにFUSEでディレクトリを作っているだけなんだなぁ.
mp (マウントポイント)
|__ db/
|__ queries/
|__ tags/
|__ tag1/
| |__ files/
| |__ tag2/
| |__ files/
|__ tag2/
|__ files/
|__ tag1/
|__ files/
② BFS(Be File System)
BeOS(現在はHaikuに引き継がれている)の専用ファイルシステムです.ファイルに属性を付与し,インデックスを作成したり,クエリ機能も有しており,データベース的な検索機能を提供しています.
ポイント
- 属性をファイルシステムが直接保持する
- 属性はOS,アプリケーション,ユーザが自由に追加できる
- 属性に対してインデックスを作成できる
- Query機能により属性検索を実行できる
- 検索結果は仮想的なフォルダとして表示される
検索の仕組み
検索用の独自ファイル(Queryファイル)に検索条件を記述し,そのファイルを実行すると検索結果を仮想ディレクトリとして提示します.
具体的な処理
具体的な処理をソースコードから見てみます.長いので,興味のない方は次章4. なぜ流行らなかったのかに進んでください.
Haikuソースコード
見たい方はこちら
アプリが検索APIを呼び出した時の処理は以下の流れで行われています.BQuery::Fetch()
↓
_kern_open_query(...)
↓
query_open(...)
↓
FS_MOUNT_CALL(open_query)
↓
bfs_open_query(...)
↓
Query::Create()
↓
Query::_Init()
↓
Expression<QueryPolicy>::Init()
↓
fd = get_new_fd(...)
BQuery::GetNextEntry()
↓
_kern_read_dir()
↓
bfs_read_query(...)
↓
Query::GetNextEntry()
↓
内部で状態を進める
↓
1件ヒットしたinodeを返す
① システムコールによって検索ストリームを開く
BQuery::Fetch() は内部で _kern_open_query() を呼び出し,検索要求をカーネルへ渡します.検索条件も文字列で渡します.
fQueryFd = _kern_open_query(...)
② ファイルシステムへ
FS_MOUNT_CALL(mount, open_query, ...)
カーネルは対象ボリュームのファイルシステム(まぁ要はBFS)に処理を明け渡します.これから分かる通り,検索がファイルシステム依存の機能として存在することが分かりますね.
③ BFSにおけるQueryオブジェクト生成
BFSでは以下の検索コンテキストが生成されます.
Query::Create(...)
これは検索結果でなく,検索条件を保存するQueryオブジェクトを生成しています.
④ クエリの構文解析(QueryParser)
さらに内部では検索条件式が構文解析されます
Expression<QueryPolicy>::Init(queryString, &position);
最終的にBFSは,検索条件を単なる文字列ではなく 構文木(条件)と、それを評価するための検索状態(インデックスやボリューム情報)として保持します.
要は,この2つです.
- 条件そのもの
- 条件を適用する対象の環境
環境が変われば結果が変わってしまうので,環境も入れとくって感じですかね?
⑤ 初期化を完了し,fdを発行する
fd = get_new_fd(...)
このfdは実際に検索を実行する際に使用します.fdは検索結果をストリームとして取得するためのハンドルです🚗💨.
⑥ 検索(インデックスを利用して効率的に条件に一致するエントリを走査する)
BQuery::GetNextEntry(...)
この関数が呼ばれると,Queryオブジェクトの内部を移動しながら条件を比較していく.実際に合致するものを一個一個見ていくんですね.
BFSはストリーミング(逐次取得型)の検索インターフェース
4. なぜ流行らなかったのか
一時期はBeOSがMacOSやWindowsに代わる次世代OSと期待されていたのに...
Semantic FileSystemは当時としては非常に先進的なアイデアでしたが,主流のファイルシステムになることはありませんでした.
理由は大きく2つあります.
① 当時は重かった
SFSでは
- 属性抽出
- インデックス作成
- インデックス更新
更新時にこれらの処理が発生します.
しかし,当時の計算機性能ではこれらの処理は決して軽くありませんでした.
② 既存のファイルシステムが進化した
一方で階層型ファイルシステムも進化しました.
- CPU性能向上
- ストレージ高速化
- 検索インデックスの普及
により,検索自体が高速になりました.
現在ではWindows SearchやSpotlightなどの検索機能により,ユーザはディレクトリ構造を意識せずにファイルへ到達できる場面が増えました.
検索によってファイルを見つけるという考え方自体は広く受け入れられたものの,SFSが提案した独自のファイルシステムへ置き換える形ではなく,既存の階層型ファイルシステムへ検索機能を追加する形で発展していったのです.
つまり,「アイディアが吸収されちゃって存在意義ない☆」 ってわけですね.
5. 現代軸で考える
お待たせしました.ここからが本題です.
Semantic FileSystemが提案されてから30年以上が経過しました.では,彼らが描いた未来は実現したのでしょうか.
Semantic FileSystemが描いた未来は実現したのか
検索は確かに普及した
現在では,検索は非常に身近な存在となっています.
- Windows SearchやSpotlightによるローカル検索
- Google DriveやOneDriveなどのクラウドストレージでの検索機能
- SNSのハッシュタグ
- 写真管理アプリのタグ
この点において,Semantic FileSystemが掲げた「属性を用いて情報へアクセスする」という考え方は,現代にも受け継がれていると言えます.
しかしフォルダは消えなかった
一方で,現在でも多くのユーザはフォルダを作成し,ファイルを整理しています.クラウドストレージにおいても,検索だけでなくディレクトリ構造による管理は広く利用されています.
つまり,検索技術は普及したものの,階層型ファイルシステムが置き換えられることはありませんでした。
なぜ?
30年以上経った現在でもフォルダが使われ続けているという事実を見ると,ディレクトリ構造のほうが人間の思考に近かったのかもしれません.(メタい理由はディレクトリ構造をずっと使ってきて,変更できなかったからってのもあったのかもしれないけど)
ディレクトリ構造は情報そのものを整理し,全体像を把握するために役立ちます.
研究
├ 発表
├ 論文
└ 実験
という構造を見るだけで,人間はファイル同士の関係性を直感的に理解できます.
一方で,Semantic FileSystemでは属性の組み合わせで情報へアクセスします.こちらの方が柔軟ではありますが,人間にとって必ずしも分かりやすいとは限りません.
また,属性ベースの管理では誰かが属性を付与しなければなりません.結局は ユーザ自身が属性の管理をする必要がありました. それならば,最初からフォルダへ整理して保存した方が楽だったのでしょう.
人間的なファイル探索を実現しようとしたのに,皮肉ですね.
Haikuについて
ここまで読んで,検索ベースのファイルシステムは歴史の中に消えてしまったのかと思われたかもしれません.実は現在でも,属性や検索を重視したファイルシステムを利用できるOSが存在します.
それがHaikuです.
Haikuは,1990年代に登場したBeOSの後継として開発されているオープンソースOSです.
- BFSを現在も活用している
- 検索ベースの思想を今でも体験できる
- GUI全体の動作が軽快
- デスクトップ用途を重視した設計
- 一貫したAPI設計
特にBeOSは当時からマルチメディア処理や応答性の高さを重視しており,「軽快なGUI」を売りにしていました.Haikuもその思想を受け継いでおり,比較的低スペックな環境でも快適に動作します.
ここでは実際に触りながら,検索機能を見ていこうと思います.
Trackerを使った検索
まずは手っ取り早くGUIで検索を試してみましょう.HaikuではTrackerというファイルマネージャーがあります.Windowsでいうエクスプローラーですね.
デスクトップでAlt+Fを入力することにより,検索のウィンドウが出ます.

検索方法は3種類あります.
- 名前で検索
- 属性で検索(名前,サイズ,更新日時で検索可能)
- 式で検索(独自属性を含む検索条件を直接記述)
「haiku」で名前検索してみましょう.
検索結果は仮想ディレクトリとして提示されます.
これで検索に触れることは可能ですが,なんかOSに組み込まれている特殊感がない.. てか属性で検索は可能ですが,肝心の属性を付与する方法がGUIでは見つからなかったので(あったら教えてください),ここからは同様の機能を使えるCLIコマンド達を使ってみます.
CLIでファイル検索
インデックスを作る
任意の属性を使うためには,インデックスの"新たな行"が必要です.それを作成します.
mkindex 属性名
属性を付与する
ファイルに属性を付与する
addattr 属性名 値 ファイル名
ファイルに付与されている属性を確認する
listattr ファイル名
ファイルに付与した属性を削除する
rmattr 属性名 ファイル名
queryコマンドで検索
query 条件式
条件式は 属性[=<>]値 の形式です.
具体的な検索処理
また具体的な処理をソースコードから見てみます.興味のない方は次章6. 感じたことに進んでください.
queryコマンドのソースコードを見ながら流れを確認してみます.
見たい方はこちら
main関数
main関数の重要なのはこの部分です
219 else
220 perform_query(volume, argv[optind], directoryPath);
221 } else {
222 // Okay, we want to query all the disks -- so iterate over
223 // them, one by one, running the query.
224 BVolumeRoster volumeRoster;
225 while (volumeRoster.GetNextVolume(&volume) == B_OK) {
226 // We don't print errors here -- this will catch /pipe and
227 // other filesystems we don't care about.
228 if (volume.KnowsQuery())
229 perform_query(volume, argv[optind], directoryPath);
230 }
231 }
ここではいろいろエラー条件を潜り抜けたのち,検索機能を使えるボリュームかどうか(まぁ要はBFSか)を確認し,それ毎にperform_query()を実行しています.
ボリュームってなんだっけとなってきたので一応確認すると,パーティションに対してファイルシステムとしてマウントした記憶領域です.ユーザから見た1つのディスクとして扱われます.「Cドライブ」「Dドライブ」みたいな.
perform_query関数
では次にperform_query関数を見てみます.
大事そうな所だけ抽出して見てみます.
①Queryオブジェクト作成
92 TFilteredQuery query;
93 query.SetVolume(&volume);
このボリュームを検索対象とします,と設定します.
②検索条件を設定
98 query.SetPredicate(predicate);
predicateに検索条件が入っているので,それを設定します.
③フィルタ追加
100 folder_params options;
101 if (filterpath != NULL && filterpath[0] != '\0') {
102 options.path = filterpath;
103 options.includeSubFolders = sSubFolders;
104 query.AddFilter(FilterByFolder, &options);
105 }
検索する範囲のパスが渡されたら,それを設定します.
④検索開始
107 status_t status = query.Fetch();
ここでは検索条件(Predicate)をカーネルに渡し、検索結果をストリームとして読み出すための仕組み(fd)を作成しています。
そして,検索が開始できたかどうかを status に格納されます。
⑤エラー処理
108 if (status == B_BAD_VALUE) {
109 // the "name=" part may be omitted in our arguments
110 BString string = "name=";
111 string << predicate;
112
113 query.SetPredicate(string.String());
114 status = query.Fetch();
115 }
検索の条件は基本 type=value の形式で受けますが,"type="が省略されていたら名前検索ということにし,"name="を勝手につけて処理を進めます.
⑥結果列挙
123 while (query.GetNextEntry(&entry) == B_OK)
ここのwhile文で条件に合致するエントリ(ファイルやディレクトリ)を1件ずつ取り出しています.リアルタイムで.
⑦パスを取得
127 entry.GetPath(&path)
/home/user/foo.txt みたいな実パスを取得します.
⑧結果表示
148 printf("%s\n", string.String());
結論
この関数の処理は下記のようにまとめられます.
検索可能なボリュームに対して検索条件(Predicate)を設定し,Fetch()で検索を実行する.
その後,GetNextEntry()を用いて検索結果を1件ずつ取得し,対応するファイルパスを表示する.
まとめるとシンプルですね.
6. 感じたこと
Semantic FileSystemの考えはとても納得するものだったけど,かみ合わなかったのだろうと思います.
つまるところ検索は,ファイルをテキトーに置いてしまう人が重宝するものだと思います.が,仕事でPCを利用してファイルを管理する人なら,テキトーにファイルを置くのはよくないし,じゃあ,プライベートでPCを使う人をターゲットにしようとしてもそもそもプライベートではあんまりファイルいじらないし.
しかし,AIが普及したことにより,ファイルに宛てる属性をAIに判断してもらったりできるかもしれません(もともとファイルから属性を決めるコストが高い). そうしたらより優れた検索ベースのOSを作ることもできそうですね.実用は...難しいと思いますが,考えるのは面白そうです.
7. おわりに
Semantic FileSystemは普及しなかった.
しかしクラウドとAIが当たり前になった現在,
当時より検索ベースのOSを実現しやすい環境が整っているようにも思えます.
私たちは本当にディレクトリに縛られ続ける必要があるのだろうか?
私はこれからもディレクトリに縛られていきますが🥴




