このエントリは2023/02/25現在の情報に基づいています。将来の機能追加や変更に伴い、記載内容からの乖離が発生する可能性があります。
まったくお初の方から、以下のような問い合わせが届いた。
AzureのBlob storageに格納したBlobをファイル名で検索したいのだが、効率的に検索する方法はないか?
ふつうにやると…
確かに、ファイル名で検索する仕組みは標準では存在しない。REST APIを使い、ファイル構造に従って検索するとしたら、List Blobsを使い、前方一致するようなフィルタを構成しprefix
に指定し、各ディレクトリを走査していくような形になってしまうが、Sequentialに実行するととんでもなく時間がかかるし、Parallelに実行したとしても、どこまでParallelにするのか、という問題が生じ、さらにコードも複雑になる。
List Blobs
https://learn.microsoft.com/rest/api/storageservices/list-blobs
なので、どうしたものか、という問い合わせが届いた次第。
Indexタグを使う
Azure Blob StorageにはIndexタグを付加できるようになっている。これを使った検索APIもあるので、こちらを使えばピンポイントで検索できる。例えば、Indexタグにファイル名を指定しておき、それを使うということも可能。
Find Blobs by Tags in Container
https://learn.microsoft.com/rest/api/storageservices/find-blobs-by-tags-container
実際にやってみる
Java SDKの場合、REST APIに相当するメソッドはBlobContainerClient
クラスもしくはBlobContainerAsyncClient
クラスにある。
BlobContainerClient
https://learn.microsoft.com/java/api/com.azure.storage.blob.blobcontainerclient
BlobContainerAsyncClient
https://learn.microsoft.com/java/api/com.azure.storage.blob.blobcontainerasyncclient
そこで、Blob storageに20個のディレクトリに100件ファイルを入れ(ファイル数としては2000個)、ファイル名を検索するのにどれぐらい時間がかかるかやってみた。Indexタグにはファイル名を付けてある。Indexタグを使うと圧倒的に検索スピードが速いと予想できるが、果たして…。

まず、List Blobを使う場合。
前方一致のためにprefix
をつけられるので、ディレクトリ部分をprefixとして指定し、List Blobを呼び出してファイルを走査することになるのだが、Javaの場合、Stream APIでfilter
を使って拾い上げることもできる。具体的にはこんな感じ。
List<BlobItem> blobItemList = blobContainerClient.listBlobs()
.stream()
.parallel()
.filter(f->f.getName().contains(filename))
.toList();
対して、Indexタグの場合。
今回はファイル名を重複させていないのであれではあるが、指定したタグに複数のファイルが紐付いていたとしても取得できる。Javaの場合はこんな感じ。
String format = """
"filename"='%s'
""";
String query = String.format(format, filename);
List<TaggedBlobItem> taggedBlobItems = blobContainerClient
.findBlobsByTags(query)
.stream()
.toList();
両者を比較すると、Indexタグを使う検索はList Blobの1/10ほどの時間で検索ができた。データ件数が多くなるともっと差が出てくる可能性がある。
全文検索もしたいなら
Cognitive SearchサービスをBlob storageに組み合わせると、全文検索の機能も付いた上で、安定した検索パフォーマンスを享受できる。Blob内の全文検索も要求事項として存在するなら利用してもよいが、今回の問い合わせのような、ファイル名検索のためだけに使うのであれば、お財布と相談しながらになろう。そのあたりは、以下のエントリにもちょいと記載してある。