JupyterでMongoDBのmongoshを扱う

# 概要
mongoshは基本CLIなので、なかなか使い勝手がそれほど良いとは言えない。Jupyterのようにプログラミングや操作の履歴が残せると学習の効果がもっと上がると思うのでJupyterでmongoshでのクエリや管理コマンドなどが実行できるように考える。これはMongoDB専用のkernelを作るということでは有りませんので誤解が無いように。Macでの説明ですが、Windowsでも可能です。

# 実行環境
```zsh
sw_vers
ProductName:		macOS
ProductVersion:		14.6.1
BuildVersion:		23G93

mongosh --version
2.3.2

mongod --version
db version v8.0.3
Build Info: {
    "version": "8.0.3",
    "gitVersion": "89d97f2744a2b9851ddfb51bdf22f687562d9b06",
    "modules": [],
    "allocator": "system",
    "environment": {
        "distarch": "aarch64",
        "target_arch": "aarch64"
    }
}

python3 --version
Python 3.12.5

jupyter --version
Selected Jupyter core packages...
IPython          : 8.26.0
ipykernel        : 6.29.5
ipywidgets       : 8.1.5
jupyter_client   : 8.6.2
jupyter_core     : 5.7.2
jupyter_server   : 2.14.2
jupyterlab       : 4.2.5
nbclient         : 0.10.0
nbconvert        : 7.16.4
nbformat         : 5.10.4
notebook         : 7.2.2
qtconsole        : 5.5.2
traitlets        : 5.14.3
```

# bashを使ってmongoshを実行
Jupyterではセルの先頭行に`%%bash`などと記述するとbashでのコマンドが実行できます。これはbashが標準入力からの文字列をbashのコマンドとして認識して実行し、その結果を標準出力に出力するためマジックコマンドとして実行が可能となります。

次のようなbashコマンドをファイルに用意してbashに実行させる。
```cmd.txt
ls -CF
```
```bash
cat cmd.txt | bash 
cmd.txt			install_compass*	mongos*
db/			mongod*
```
これをmongoshに適用します。

MongoDBのコマンド文をcmd.txtに用意して実行してみます。
```cmd.txt
show dbs
use admin
show collections
```
このファイルを使って実行します。
```bash
cat cmd.txt | mongosh -u mongo -p xyz --quiet
test> show dbs
admin   132.00 KiB
config   60.00 KiB
local    40.00 KiB
test> use admin
switched to db admin
admin> show collections
system.users
system.version
admin> %         
```
bashと同じことができました。毎回ユーザ名、パスワード、--quietのオプションを記述するのは面倒なので、これらを記述したシェルスクリプトを用意します。
Windowsではバッチファイルです。また、引数の`"$@"`もWindowsに合わせてください。
```bash:msh
mongosh -u mongo -p xyz --quiet "$@"
```
`mongosh`の他のオプションも指定できるように`"$@"`と実行できるように`chmod +x msh`を忘れないように。Windowsの場合は`msh.bat`、`msh.cmd`などとしてください。
では`cmd.txt`を実行してみます。
```bash
cat cmd.txt | msh
test> show dbs
admin   132.00 KiB
config   60.00 KiB
local    40.00 KiB
test> use admin
switched to db admin
admin> show collections
system.users
system.version
admin> %      
```

次はJupyterで使って見ますが`mongosh`と`msh`が実行できるようにPATHを設定してください。`Jupyter input cell`はJupyterの入力セルの入力です。
```:Jupyter input cell
%%bash
ls
```
> Untitled.ipynb

lsコマンドが実行され、ファイル一覧が出力されました。 

まずは、bashを使ってmongoshを動かしてみます。

```:Jupyter input cell
%%bash -c mongosh
show dbs
```
> Current Mongosh Log ID:	672819ea49bbcb58e0e9ea4d
Connecting to:		mongodb://127.0.0.1:27017/?directConnection=true&serverSelectionTimeoutMS=2000&appName=mongosh+2.3.2
Using MongoDB:		8.0.3
Using Mongosh:		2.3.2
mongosh 2.3.3 is available for download: https://www.mongodb.com/try/download/shell
>
> For mongosh info see: https://www.mongodb.com/docs/mongodb-shell/
>
> MongoServerError[Unauthorized]: Command listDatabases requires authentication
> test> 

ユーザ、パスワードを指定しなかったので認証エラーになりました。

```:Jupyter input cell
%%bash -c 'mongosh -u mongo -p xyz --quiet'
show dbs
```
> test> admin   132.00 KiB
 config   60.00 KiB
 local    40.00 KiB
 test> 

 今度は正常に処理されました。入力したコマンドは出力されず、先頭行はプロンプトとコマンドの出力が被ってしまっています。これが、１つ目のデメリットです。またmongoshは実行後に終了するので、Jupyterのセル間の引き継ぎは行われません。これが２つ目のデメリットです。それでもJupyterを使うメリットは十分にあると思います。
 `mongosh`を使うと毎セル事に長い記述が面倒ですし、何よりパスワードが丸みえです。ここは`msh`を使うときです。

 ```:Jupyter input cell
 %%bash -c msh
show dbs
print("-----------------------")
use admin
print("-----------------------")
show collections
```

> test> admin   132.00 KiB
config   60.00 KiB
local    40.00 KiB
test> -----------------------
>
> test> switched to db admin
admin> -----------------------
> 
> admin> system.users
system.version
admin> 

`print`を挟んだのはコマンド毎の出力を明確にするためです。

# mongoshやmshをマジックコマンドとしてJupyterに登録
ここではJupyterのソースの一部を修正しますので、`bash`を使った方法で良ければここは飛ばしてください。
Jupyterのソースのどこを修正するかは`bash`がどこに登録されているかを確認してそこに`mongosh`や`msh`を登録します。　確認方法を紹介します。マジックコマンドの後に`?`を付けて実行してください。

 ```:Jupyter input cell
 %%bash?
 ```
> Docstring:
%%bash script magic
>
> Run cells with bash in a subprocess.
>
> This is a shortcut for `%%script bash`
File:      ~/.pyenv/versions/3.12.5/lib/python3.12/site-packages/IPython/core/magics/script.py

最後に表示された`script.py`を修正します。

```script.py
@default('script_magics')
    def _script_magics_default(self):
        """default to a common list of programs"""

        defaults = [
            'sh',
            'bash',
            'perl',
            'ruby',
            'python',
            'python2',
            'python3',
            'pypy',
            'mongosh',  #追加
            'msh',      #追加
        ]
        if os.name == 'nt':
            defaults.extend([
                'cmd',
            ])

        return defaults
```
`bash`で検索すると該当行が見つかると思います。`defaults`の配列に`mongosh`と`msh`を追加しました。

では、Jupyterを再起動して実行してみましょう。
 ```:Jupyter input cell
 %%msh
 show dbs
 use admin
 show collections
 ```
> test> admin   132.00 KiB
config   48.00 KiB
local    40.00 KiB
test> switched to db admin
admin> system.users
system.version
admin> 

なぜか`config`サイズが小さくなっていますが、実行は問題ないようです。

# MongoDBのドキュメントに記載されているコマンドの実行例

以下のサイトに例を実行してみます。

https://www.mongodb.com/ja-jp/docs/mongodb-shell/crud/insert/

jupyter-labで実行したスクショです。

![jupyter_mongosh.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/258007/aafe7a30-6a9c-2310-83db-e4ce0db5c1f2.png)

注意点としてはドキュメントでセル[2]の`find`は`use sample_mflix`が有りませんでしたが追加しました。前述したようにセル間は引き継がれないのでセル[2]でまた`use sample_mflix`の記述が必要になります。まあ、`find`もセル[1]に記述しておけば良いだけすが。

例えば、使うDBが決まっているのであれば`msh`に`use xxxx`を入れておけばJupyter でuse xxxxを書かなくて良い。あるいは`.mongoshrc.js`に記述すればよい。
```bash:msh
mongosh -u mongo -p xyz --quiet --shell --eval 'use xxxx' "$@"
```
`--shell`がないと`--eval`を実行後すぐ終了してしまう。

# 終わりに

これはMongoDBに限らず他のCLIのソフトウェアで同じように使える可能性があります。


以前の投稿した記事がありますので、こちらも参考にしてください。
[Jupyterのセルmagicである%%bashや%%cmdと同じように新しい%%scriptのショートカットの登録](https://qiita.com/h6591/items/31ce03d3b4b3fe0bce01)