# Learn how to connect and run script on mongosh

Recently, I needed to write some scripts to run on `mongosh` and since there will be common code among the scripts. I was wondering if it is possible to write just like any JavaScript file (with import/export) to run on `mongosh`.

I also explored a little further to see what are supported as well, read below to know more!

# Assumption

You have a local instance of `MongoDB` installed, with a username as `root` and password as `password`

# MongoDB Shell (mongosh)

`mongosh` allows you to connect and work with `MongoDB` via CLI. According to its [website](https://www.mongodb.com/docs/mongodb-shell/), `mongosh` is a

>  fully functional JavaScript and Node.js 16.x REPL environment for interacting with MongoDB deployments

Given that, it would have meant I can write a JavaScript file, and expects it to run perfectly on `mongosh`. And I should be able to `import and export` statement and expect it to run as well.

## Installation

Grab a copy of `mongosh` at its [official website](https://www.mongodb.com/try/download/shell). For my case, I'm using `Windows`, and I will download the `zip` binary.

![image.png](https://cdn.hashnode.com/res/hashnode/image/upload/v1665080580991/mTpfbEXBl.png align="left")

Once downloaded, extract it to any path of your choice, and add it to your environment variable.

# Using mongosh

Let's see how we can use `mongosh` via command line first, before looking into running via scripts.

## Connecting

Launch your command prompt, and run 

```
mongosh "mongodb://localhost:27017/sample" --username root --password password --authenticationDatabase admin
```

You should see the following after connected

```
> mongosh "mongodb://localhost:27017/sample" --username root --password password --authenticationDatabase admin

Current Mongosh Log ID: 634138e49092fab94899061f
Connecting to:          mongodb://<credentials>@localhost:27017/sample?directConnection=true&serverSelectionTimeoutMS=2000&authSource=admin&appName=mongosh+1.6.0
Using MongoDB:          6.0.1
Using Mongosh:          1.6.0

For mongosh info see: https://docs.mongodb.com/mongodb-shell/

------
   The server generated these startup warnings when booting
   2022-09-24T06:48:19.570+00:00: Using the XFS filesystem is strongly recommended with the WiredTiger storage engine. See http://dochub.mongodb.org/core/prodnotes-filesystem
   2022-09-24T06:48:21.727+00:00: /sys/kernel/mm/transparent_hugepage/enabled is 'always'. We suggest setting it to 'never'
   2022-09-24T06:48:21.727+00:00: vm.max_map_count is too low
------

------
   Enable MongoDB's free cloud-based monitoring service, which will then receive and display
   metrics about your deployment (disk utilization, CPU, operation statistics, etc).

   The monitoring data will be available on a MongoDB website with a unique URL accessible to you
   and anyone you share the URL with. MongoDB may use this information to make product
   improvements and to suggest MongoDB products and deployment options to you.

   To enable free monitoring, run the following command: db.enableFreeMonitoring()
   To permanently disable this reminder, run the following command: db.disableFreeMonitoring()
------

sample>
```

If you get error such as 

```
MongoServerError: Authentication failed.
```

Ensure to double-check your credentials / authenticationDatabase value.

## Inserting Documents

To create a document in `sample` database, simply run the following command after connected, and you will see the acknowledgement immediately

```
sample> db.sample.insertOne({"name":"sample-1"})
{
  acknowledged: true,
  insertedId: ObjectId("634139d14c429a502d43ee3b")
}
```

## Finding Documents

To search for all document, run the following command

```
sample> db.sample.find();
[
	{
		_id: ObjectId("634139d14c429a502d43ee3b"),
		name: 'sample-1'
	}
]
```

## Execute with JavaScript file

So far, we are interacting with `mongosh` via the shell and that's fine for demo, or simple use-case. But what if we have complex scripts that we want to run, or that we want to run it automatically via a cron job? 

`mongosh` does accept a `--file` parameter to take in a `javascript` based file. Imagine our `index.js` looks like this

```
printjson(db.sample.insertOne({"name":"sample-2"}));
printjson(db.sample.find());
```

> `printjson` is a `mongosh` specific function that returns formatted JSON

In order to run the script on connect, we run the following command

```
mongosh "mongodb://localhost:27017/sample" --username root --password password --authenticationDatabase admin --file index.js
```

Notice the new parameter `--file index.js` I have passed in

The output would look like

```
Loading file: index.js
{
  acknowledged: true,
  insertedId: ObjectId("63413cae8d616c98fd852c7d")
}
[
  { _id: ObjectId("634139d14c429a502d43ee3b"), name: 'sample-1' },
  { _id: ObjectId("63413cae8d616c98fd852c7d"), name: 'sample-2' }
]
```

## Execute with JavaScript file (with imports)

As I mentioned previously that given a complex script, there would probably have some functions or objects shared across scripts, and it would be helpful to use those in my main (index.js) file

Now, let's assume we have two files now, `index.js` and `collection.js`

```js
// index.js
const profile = require('./collections');

printjson(db.sample.insertOne(profile));
printjson(db.sample.find());

// collections.js
const profile = {
    name: 'name',
    remarks: 'remarks'
};

module.exports = { profile };
```

Notice that I've exported the `profile` object from `collection.js` and then import (require) it on `index.js`. However, when I run the script, you need to just specify the entry file which in this case would be `index.js`

```
mongosh "mongodb://localhost:27017/sample" --username root --password password --authenticationDatabase admin --file index.js
```

And the output will be

```
Loading file: index.js
{
  acknowledged: true,
  insertedId: ObjectId("6346d4b40ce71b9819a17d61")
}
[
  {
    _id: ObjectId("6346d4b40ce71b9819a17d61"),
    profile: { name: 'name', remarks: 'remarks' }
  }
]
```

## Execute with load function

If you wish to load and run the script within the `mongosh` console, you can do so via the `load` function. Connect to the shell as usual without specifying the `--file` parameter

```
mongosh "mongodb://localhost:27017/sample" --username root --password password --authenticationDatabase admin
```

Once entered to the console mode, run `load('index.js')`

```sh
sample> load('index.js')
{
  acknowledged: true,
  insertedId: ObjectId("6346d54fd955e733e1cad13e")
}
[
  {
    _id: ObjectId("6346d54fd955e733e1cad13e"),
    profile: { name: 'name', remarks: 'remarks' }
  }
]
true
```

## Support for native NodeJS module

If you ever need to rely on [nodejs modules](https://nodejs.org/dist/latest-v16.x/docs/api/), then you will be glad to know that it is supported as well!

```js
// index.js
const os = require('node:os');

print(os);
```

```
sample> load('index.js')
{
  arch: [Function: arch] {
    [Symbol(Symbol.toPrimitive)]: [Function (anonymous)]
  },
  cpus: [Function: cpus],
  endianness: [Function: endianness] {
    [Symbol(Symbol.toPrimitive)]: [Function (anonymous)]
  },
  // omitted
  },
  EOL: '\r\n',
  devNull: '\\\\.\\nul'
}
sample>
```

## Support for 3rd party modules

You can rely on 3rd party modules too, but the condition is that the module you want to use, must be installed `globally or locally`.

### Global

Let's install `lodash` module globally

```sh
npm i -g lodash
```

Which, in my case, will be installed to `%APPDATA%\npm\node_modules`

```js
// index.js
const path = require('node:path');
// this might be slightly different for linux machine
const lodashPath = path.join(process.env.APPDATA, 'npm', 'node_modules', 'lodash');
const lodash = require(lodashPath);

print(lodash);
```

1. Import `path` module to make use of `join` method
2. Join the path segment using the platform-specific separator as a delimiter
3. Import `lodash`

```js
sample> load('index.js')
<ref *1> [Function: lodash] {
  templateSettings: {
    escape: /<%-([\s\S]+?)%>/g,
    evaluate: /<%([\s\S]+?)%>/g,
    interpolate: /<%=([\s\S]+?)%>/g,
    variable: '',
    imports: { _: [Circular *1] }
  },
  after: [Function: after],
  ary: [Function: ary],
  // omitted
```

If you encounter error such as

```sh
sample> load('index.js')
Uncaught:
Error: Cannot find module 'lodash'
Require stack:
- <repl>
sample>
```

This means that it can't reference to the global `lodash` module that was installed, so it is likely that the path is wrong. So make sure you double-check on that.

### Local

The directory where you run the script must have a `node_modules` directory containing the library/module you want to use.

For this, we do not need to point to the global npm path, but just need to `import` as usual

```js
// index.js
const lodash = require('lodash');

print(lodash);
```

And everything should run normally as before

# Conclusion

We looked at how `mongosh` can be used to

- run commands via the `console` directly
- run script using `--file` parameter
- run script using`load` function within the `console`
- run script that imports other files, native NodeJS module, or even 3rd party modules

Personally, I like the option of using `--file` to run because I can write the script using JavaScript, and more importantly, I can also choose to run using `node index.js` if I want to. 

I hoped that this gives you an insight of what `mongosh` can do and help you to improve your experience using the console.

# References

- [mongodb-shell](https://www.mongodb.com/docs/mongodb-shell/write-scripts/)

