ObsidianをPowerShellから検索

# Obsidianとは

マークダウンでノートが取れる高機能ナレッジベースアプリです。ノート間のリンクなど基本的な機能は一通り揃っていて、開発もそれなりに盛んで、プラグインもそこそこ揃っていて、自分でJavaScript, CSSを書くことでカスタマイズもそれなりに出来ます。日本語にも対応しています。基本的にデータはマークダウンになってるので、Pythonなどでサクッとゴニョゴニョすることも容易ですね。モバイル版もあります。

# この記事で分かること

この記事ではWindowsを前提に、PowerShellからノートを簡単に検索する方法を説明します。
なお筆者はPowerShellは殆ど知らないので、間違いの指摘や意見など下さると幸いです。


# Obsidianの検索

Obsidianでは通常の検索以外に、URLを用いて外部からも検索できます。

例えば、gameというデータベース(vaultと呼ばれる)から「ぷよぷよ」で検索したい場合、gameというデータベースを開いた状態でCtrl+Shift+F→ぷよぷよ→Enterで検索できますが、これと同じことをブラウザから

```
obsidian://vault=game&query=ぷよぷよ
```

で検索できます。vaultを省略すると最後に開かれたvaultが使われるようです。

コマンドプロンプトからでもstartコマンドで実行できます。

```
start obsidian://vault=game&query=ぷよぷよ
```

ただし、これには罠があって、空白文字を含むURLの場合はダブルクォートで括るだけでなく、第一引数にウィンドウ名を指定する必要があります。これはstartコマンドには、クォートされた引数をウィンドウ名と解釈するという~~奇怪な~~独特な仕様があるからです。

```
start "dummy" "obsidian://vault=game&query=ぷよぷよ 連鎖"
```

## タグ検索の方法

タグ検索はqueryに"tag:foo"の形で指定できます。複数のタグを指定する場合は、"tag:foo tag:bar"と書きます。

```
obsidian://vault=game&query=tag:ぷよぷよ tag:連鎖 計算法
```

Obsidian内から検索する時も同様です。

```
tag:ぷよぷよ tag:連鎖 計算法
```

これはちょっと面倒ですね。タグ検索は頻繁に使用するものなので、これを簡易化したい。
そこで、PowerShellから検索するコマンドを作ってみましょう。


# PowerShellコマンドを作る


PowerShellのプロファイル(例えばMicrosoft.PowerShell_profile.ps1)にObsidianのノートを検索するコマンドを書きます。そうすれば、コマンドラインから呼び出して使えます。

ここでは非同期実行するためにコマンドプロンプト経由で呼びだします。

名前に用いる動詞は"Start"が適切なようです(参照　[PowerShellコマンドに承認されている動詞](https://docs.microsoft.com/ja-jp/powershell/scripting/developer/cmdlet/approved-verbs-for-windows-powershell-commands))。よってコマンドの名前は、Start-ObsidianSearchとします。

コマンドの記述は次の通りです。


```powershell
function Start-ObsidianSearch(){
    <#
    .SYNOPSIS
    Obsidianで検索

    .EXAMPLE
    Start-ObsidianSearch ぷよぷよ
    Start-ObsidianSearch -t tag1,tag2 -v game ぷよぷよ
    #>

	[CmdletBinding()]
	Param(
		[parameter(ValueFromRemainingArguments, Mandatory=$false, Position=0)]
		[string] $query,
		[parameter()]
        [alias("v")]
		[string] $vault="myvault", # 最もよく使うvault名を既定値に指定
		[parameter()]
        [alias("t")]
		[string[]] $tags
	)

    if($tags.length -gt 0){
        $tags = $tags -split ",\s*" | %{ "tag:" + $_ }
        $tags = $tags -join " "
        $query += " " + $tags
    }
    $url = "obsidian://search?vault=$vault&query=$query"
    $url

    start cmd -ArgumentList "/c","start `"dummy`" `"$url`""
}
```

関数の先頭などにコメントを記述しておくと、メタデータとして解釈され、Get-Helpによるヘルプの表示時に参照できます。.SYNOPSISは簡潔な説明を書きます。コマンドの場合は.EXAMPLEで使い方も書いておくとよいでしょう。.PARAMETERにパラメーターの説明を書くこともできます。

[CmdletBinding()]は関数をコマンドとして扱う場合の書き方です。詳しくは分かりませんがコンパイルされてコマンドとして登録されるみたいです。

それに続くParam()部分はパラメーターの型や既定値や必須か否かなどの指定です。コマンド実行する際にはパラメーターはここでの変数名をそのまま使って、

```
> Start-ObsidianSearch -vault game
```

のように指定します。これにはエイリアスも指定できます。ここでは"v"としていますが、この場合はパラメーター名"vault"に前方一致するので指定しなくても構いません。PowerShellがパラメーター名を柔軟に解釈してくれます。

split, joinはこのサイトの読者なら大体分かると思うので省きます。| %{ } はJavaScriptでいうmapです。

$urlだけの行は誤植ではなく、どんなURLが開かれたかをコンソールに出力しています。

最後のstartコマンドでは引数を指定するために-ArgumentListを指定しています。その中でcmd.exeのstartコマンドを呼んでいます。startコマンドにダブルクォートつきの引数を渡すために、バックチック(`)を前置してPowerShell側にエスケープを指示しています。


# エイリアスでコマンドを呼ぶ

コマンド名が長いというのはPowerShellに対してよく言われる文句ですが、エイリアスが使えるので問題ありません。

Set-Aliasというコマンドでエイリアスを定義できます。ちなみにSet-Aliasのエイリアスとして既定で"sal"が用意されています。

```powershell
sal o Start-ObsidianSearch
```

こうすると、コマンドラインで

```
> o -t game ぷよぷよ
```

のように短いキーストロークで検索できるようになるわけですね。