目次
概要
Azure Functions で形態素解析を行うため MeCab.DotNet を使用します。こちらのライブラリはローカルディレクトリから辞書ファイルを読み込むため、Azure Blob Storage にアップロードした各種ファイルを Azure Functions の TMP
ディレクトリ配下にダウンロードして利用するようにします。
容量制限やライフサイクルなどは考慮が必要です。
WPFで動かす記事やユーザー辞書を追加する前回の記事など書いています。
インストール
Azure Functions のプロジェクトを作成したら、NuGet で「MeCab.DotNet」と「Azure.Storage.Blobs」をインストールします。
MeCab.DotNet は「MeCab」、「NMeCab」を .NET Core に移植したパッケージです。
以下パッケージサイトから抜粋。
"MeCab" は、日本語形態素解析エンジンのプロジェクトです。
"NMeCab" は、上記MeCabを、.NET Framework 2.0のマネージライブラリとして実装し直したものです。ただ、もう更新されていないようです...
"MeCab.DotNet" (このプロジェクト)は、上記NMeCabを最新の.NET Core 1/2/3と.NET Frameworkで使えるように移植し、NuGetのパッケージに固めて使いやすくしたものです。
なお、ライセンスは GPL2 または LGPL2.1 とのことで利用に際しては注意が必要です。
サンプル実装
まずは Json を受けるためのパラメータクラスです。
public class TextAnarytics { public bool DeleteCacheDics { get; set; } public bool ForceDownload { get; set; } public string[] UserDics { get; set; } public string Text { get; set; } }
Text
が解析対象の文字列です。
ユーザー辞書はパラメータで指定するようになっています。
一度ダウンロードした辞書を刷新するために TMP
ディレクトリから全辞書を削除するフラグと、ユーザー辞書などを更新するために上書きをするフラグを用意してあります。
続いて、Function です。
[FunctionName("Function1")] public static async Task<IActionResult> Run( [HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req, ILogger log) { log.LogInformation("C# HTTP trigger function processed a request."); var requestBody = await new StreamReader(req.Body).ReadToEndAsync(); var data = JsonConvert.DeserializeObject<TextAnarytics>(requestBody); var deleteCacheDics = data.DeleteCacheDics; var forceDownload = data.ForceDownload; var userDics = data.UserDics; var text = data.Text; if (deleteCacheDics) { DeleteDir(); } await Task.WhenAll( Download("char.bin", forceDownload), Download("dicrc", forceDownload), Download("matrix.bin", forceDownload), Download("sys.dic", forceDownload), Download("unk.dic", forceDownload)); if(userDics?.Length > 0) { var tasks = new List<Task>(); foreach (var dic in userDics) tasks.Add(Download(dic, forceDownload)); await Task.WhenAll(tasks); } var result = ""; if (!string.IsNullOrEmpty(text)) { var dicDir = Directory.CreateDirectory(Path.Combine(Environment.GetEnvironmentVariable("TMP"), "dic")); var param = new MeCabParam(); param.DicDir = dicDir.FullName; param.UserDic = userDics ?? param.UserDic; var tagger = MeCabTagger.Create(param); foreach (var node in tagger.ParseToNodes(text)) { if (0 < node.CharType) { result += $"{node.Surface}\t{node.Feature}\r\n"; } } } string responseMessage = string.IsNullOrEmpty(result) ? "This HTTP triggered function executed faild." : result; return new OkObjectResult(responseMessage); }
TMP
ディレクトリから dic
フォルダ毎削除するコードです。
private static void DeleteDir() { Directory.Delete(Path.Combine(Environment.GetEnvironmentVariable("TMP"), "dic"), true); }
最後に辞書をダウンロードして TMP
ディレクトリに保存するコードです。
private static string connectionString = "YOUR CONNECTION STRING"; private static string blobContainerName = "dic"; private static async Task Download(string blobName, bool force = false) { var dicDir = Directory.CreateDirectory(Path.Combine(Environment.GetEnvironmentVariable("TMP"), "dic")); var dicFile = Path.Combine(dicDir.FullName, blobName); if(!force && File.Exists(dicFile)) { return; } var blobServiceClient = new BlobServiceClient(connectionString); var blobContainerClient = blobServiceClient.GetBlobContainerClient(blobContainerName); var blobClient = blobContainerClient.GetBlobClient(blobName); using (var stream = new MemoryStream()) { var response = await blobClient.DownloadToAsync(stream).ConfigureAwait(false); using (var file = new FileStream(dicFile, FileMode.Create, FileAccess.Write)) { stream.Position = 0; await stream.CopyToAsync(file); } } }
プロジェクト設定
ユーザー辞書を使用するためには csproj
を開いて、PropertyGroup
に以下の1行を追加します。
<MeCabUseDefaultDictionary>false</MeCabUseDefaultDictionary>
Azure Blob Storage にアップロード
MeCab.DotNet に含まれている以下のファイルを Azure Blob Storage にアップロードします。
- char.bin
- dicrc
- matrix.bin
- sys.dic
- unk.dic
加えて、前回の記事で作成したユーザー辞書もアップロードしておきます。
- userdic1.dic
- userdic2.dic
実行
プロジェクトをサーバーにデプロイします。
ちなみにローカル実行した場合の TMP
ディレクトリは C:\Users\<YOURNAME>\AppData\Local\Temp
になります。
デプロイが終わったら、Postman などを使って 以下の Json を投げてみます。
{ "DeleteCacheDics" : false, "ForceDownload" : false, "Text" : "はじめまして。 山田太郎、山田が苗字で、太郎が名前です。 日本の大阪出身です。 私は、1973年4月14日生まれです。山田花子は姉です。", }
システム辞書のみなので、「山田太郎」が「山田」と「太郎」で分解されます。
続いて、ユーザー辞書を指定します。
{ "DeleteCacheDics" : false, "ForceDownload" : false, "Text" : "はじめまして。 山田太郎、山田が苗字で、太郎が名前です。 日本の大阪出身です。 私は、1973年4月14日生まれです。山田花子は姉です。", "UserDics" : ["userdic1.dic", "userdic2.dic"] }