An open API service indexing awesome lists of open source software.

https://github.com/variety/variety

Variety: a MongoDB schema analyzer
https://github.com/variety/variety

cli database-tools mongodb mongodb-schema mongosh nodejs schema-analyzer

Last synced: 19 days ago
JSON representation

Variety: a MongoDB schema analyzer

Awesome Lists containing this project

README

          

# Meet Variety, a Schema Analyzer for MongoDB

[![OpenSSF Scorecard](https://api.scorecard.dev/projects/github.com/variety/variety/badge)](https://scorecard.dev/viewer/?uri=github.com/variety/variety) [![CodeQL](https://github.com/variety/variety/actions/workflows/codeql.yml/badge.svg)](https://github.com/variety/variety/actions/workflows/codeql.yml) [![MIT License](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)

This lightweight tool helps you get a sense of your application's schema, as well as any outliers to that schema. Particularly useful when you inherit a codebase with a data dump and want to quickly learn how the data's structured. Also useful for finding rare keys.

***

_“I happen to slowly be falling in love with Variety! It is actually one of the most useful tools to get a sense for a messy/unknown data set, and I have put it in a few of our exercises at Zipfian Academy.”_

Jon Dinu
_Co-founder of [Zipfian Academy](https://www.zipfianacademy.com/)_

***

_“This code saved me at least an hour of work, so least i could do was spend a minute to [say thanks](https://github.com/variety/variety/issues/107#issue-129803853). Thank you.”_

[@a-c-m](https://github.com/a-c-m)

***

Also featured on the [official MongoDB blog](https://web.archive.org/web/20231002225312/http://www.mongodb.com:80/blog/post/meet-variety-a-schema-analyzer-for-mongodb).

## Tested Runtimes

| Runtime | Tested Release Lines |
| --- | --- |
| MongoDB | latest 8.2.x, 8.0.x, 7.0.x, and 5.0.x |
| MongoDB shell | `mongosh`: MongoDB >= 6.0
`mongo`: MongoDB < 6.0 |
| Node.js | latest 22.x, plus a latest 24.x smoke test |

## BSON Type Support Snapshot

The detailed BSON support inventory lives in
[docs/bson-type-inventory.md](docs/bson-type-inventory.md). At a glance,
Variety has intentional, test-backed behavior for every storage-level BSON
value type, though some values are intentionally reported under broader
Variety labels such as `Number`, `Code`, `String`, `DBRef`, or subtype-specific
`BinData-*` labels rather than one BSON name per output label.

| Area | Current snapshot |
| --- | --- |
| Top-level BSON value types | `21 / 21` storage-level BSON value types have intentional, test-backed Variety behavior. Some intentionally merge into broader output labels such as `Number`, `Code`, `String`, `DBRef`, or per-subtype `BinData-*` labels. |
| Standard BSON binary subtypes | `10 / 10` standard binary subtypes (`0x00` through `0x09`) have intentional, test-backed Variety behavior. |
| User-defined binary subtypes | `128 / 128` user-defined subtypes (`0x80` through `0xFF`) are intentionally mapped as `BinData-user[0xNN]`, with representative test coverage for `0x80`, `0x81`, and `0xff`. |
| Vector subtype `9` dtypes | `3 / 3` documented dtypes currently get dtype-specific labels: `BinData-vector[INT8]`, `BinData-vector[PACKED_BIT]`, and `BinData-vector[FLOAT32]`. Unknown dtype bytes and malformed payloads also have explicit fallback labels. |

## An Easy Example

We'll make a collection:

db.users.insertMany([
{name: "Tom", bio: "A nice guy.", pets: ["monkey", "fish"], someWeirdLegacyKey: "I like Ike!"},
{name: "Dick", bio: "I swordfight.", birthday: new Date("1974/03/14")},
{name: "Harry", pets: "egret", birthday: new Date("1984/03/14")},
{name: "Geneviève", bio: "Ça va?"},
{name: "Jim", someBinData: new BinData(2,"1234")},
]);

So, let's see what we've got here:

$ mongosh test --eval "var collection = 'users'" variety.js

+--------------------------------------------------------------------+
| key | types | occurrences | percents |
| ------------------ | -------------------- | ----------- | -------- |
| _id | ObjectId | 5 | 100.0 |
| name | String | 5 | 100.0 |
| bio | String | 3 | 60.0 |
| birthday | Date | 2 | 40.0 |
| pets | String (1),Array (1) | 2 | 40.0 |
| someBinData | BinData-old | 1 | 20.0 |
| someWeirdLegacyKey | String | 1 | 20.0 |
+--------------------------------------------------------------------+

_(`test` is the database containing the collection we are analyzing.)_

These examples use `mongosh`. If your environment still ships the legacy `mongo`
shell instead, substitute that executable in the commands below.

Hmm. Looks like everybody has a `name` and `_id`. Most, but not all have a `bio`.

Interestingly, it looks like `pets` can be either an array or a string, but there are more arrays than strings. Will this cause any problems in the application, I wonder?

Seems like the first document created has a weird legacy key—those damn fools who built the prototype didn't clean up after themselves. If there were a thousand such early documents, I might cross-reference the codebase to confirm they are no longer used, and then delete them all. That way they'll not confuse any future developers.

By default, Variety prints results to standard output. If you want to store them in MongoDB for later use, see [Save Results in MongoDB For Future Use](#save-results-in-mongodb-for-future-use).

## See Progress When Analysis Takes a Long Time

Variety does not print its own progress bar or "percent complete" measurement.

For long-running analyses, watch the MongoDB server logs instead. If MongoDB
reports progress for the underlying work, it will appear in `mongod`'s logs,
not in Variety's output.

Where those logs live depends on how MongoDB is running: they may be in a log
file, available through `journalctl`, or exposed by your container runtime.

Some MongoDB versions and logging configurations do not emit a percentage for
these operations. If you do not see one, Variety may still be running normally;
it just means MongoDB is not exposing that measurement in your environment.

## Analyze Only Recent Documents

Perhaps you have a really large collection, and you can't wait a whole day for Variety's results.

Perhaps you want to ignore a collection's oldest documents, and only see what the collection's documents' structures have been looking like, as of late.

One can apply a `limit` constraint, which analyzes only the newest documents in a collection ([unless sorting](#analyze-documents-sorted-in-a-particular-order)), like so. Note that `limit` controls _which documents are analyzed_ and therefore affects occurrence counts and percentages — it is not the same as [`maxExamples`](#collect-multiple-example-values), which controls how many sample values appear per key in the output without affecting which documents are scanned.

$ mongosh test --eval "var collection = 'users', limit = 1" variety.js

Let's examine the results closely:

+----------------------------------------------------+
| key | types | occurrences | percents |
| ----------- | ----------- | ----------- | -------- |
| _id | ObjectId | 1 | 100.0 |
| name | String | 1 | 100.0 |
| someBinData | BinData-old | 1 | 100.0 |
+----------------------------------------------------+

We are only examining the newest document here (`limit = 1`, using Variety's default `_id: -1` sort). It belongs to Jim, and only contains the `_id`, `name`, and `someBinData` fields. So it makes sense these are the only three keys.

## Analyze Documents to a Maximum Depth

Perhaps you have a potentially very deep nested object structure, and you don't want to see more than a few levels deep in the analysis.

One can apply a `maxDepth` constraint, which limits the depth Variety will recursively search to find new objects.

db.users.insertOne({name:"Walter", someNestedObject:{a:{b:{c:{d:{e:1}}}}}});

The default will traverse all the way to the bottom of that structure:

$ mongosh test --eval "var collection = 'users'" variety.js

+----------------------------------------------------------------+
| key | types | occurrences | percents |
| -------------------------- | -------- | ----------- | -------- |
| _id | ObjectId | 1 | 100.0 |
| name | String | 1 | 100.0 |
| someNestedObject | Object | 1 | 100.0 |
| someNestedObject.a | Object | 1 | 100.0 |
| someNestedObject.a.b | Object | 1 | 100.0 |
| someNestedObject.a.b.c | Object | 1 | 100.0 |
| someNestedObject.a.b.c.d | Object | 1 | 100.0 |
| someNestedObject.a.b.c.d.e | Number | 1 | 100.0 |
+----------------------------------------------------------------+

$ mongosh test --eval "var collection = 'users', maxDepth = 3" variety.js

+----------------------------------------------------------+
| key | types | occurrences | percents |
| -------------------- | -------- | ----------- | -------- |
| _id | ObjectId | 1 | 100.0 |
| name | String | 1 | 100.0 |
| someNestedObject | Object | 1 | 100.0 |
| someNestedObject.a | Object | 1 | 100.0 |
| someNestedObject.a.b | Object | 1 | 100.0 |
+----------------------------------------------------------+

As you can see, Variety only traversed three levels deep.

## Analyze a Subset of Documents

Perhaps you have a large collection, or you only care about some subset of the documents.

One can apply a `query` constraint, which takes a standard MongoDB query object, to filter the set of documents required before analysis.

$ mongosh test --eval "var collection = 'users', query = {'caredAbout':true}" variety.js

MongoDB shell constructors such as `ObjectId(...)` can be used inside that
evaluated query:

$ mongosh myDb --eval "var collection = 'myCollection', query = {company: ObjectId('5a6b446d46246c5b6eaaa4a4')}" variety.js

The first-party `variety DB/COLLECTION` CLI keeps `--query` limited to strict
JSON, so use `--eval` when a filter needs shell-side values:

$ variety myDb/myCollection --quiet --outputFormat json --eval "var query = {company: ObjectId('5a6b446d46246c5b6eaaa4a4')}"

Extended JSON support for `--query` is being discussed in [issue #267](https://github.com/variety/variety/issues/267). Hat tip: [@mariusbancos](https://github.com/mariusbancos) ([#146](https://github.com/variety/variety/issues/146)).

## Analyze Documents Sorted In a Particular Order

Perhaps you want to analyze a subset of documents sorted in an order other than creation order, say, for example, sorted by when documents were updated.

One can apply a `sort` constraint, which analyzes documents in the specified order like so:

$ mongosh test --eval "var collection = 'users', sort = { updated_at : -1 }" variety.js

## Include Last Value

One can also apply a `lastValue` constraint to capture one representative value for each key.

$ mongosh test --eval "var collection = 'orders', lastValue = true" variety.js

Via the first-party CLI:

$ variety test/orders --last-value

+--------------------------------------------------------------------------------------------+
| key | types | occurrences | percents | lastValue |
| --------------- | ------------ | ----------- | -------- | -------------------------------- |
| _id | ObjectId | 1 | 100.0 | 5a834b76f4d3fa6e578a67f6 |
| age | Number | 1 | 100.0 | 38.2569 |
| animals | Array | 1 | 100.0 | [Array] |
| animals.XX.type | String | 1 | 100.0 | dog |
| balance | NumberLong | 1 | 100.0 | 1236458945684846 |
| date | Date | 1 | 100.0 | 2017-12-17T19:46:09.000Z |
| fn | Object | 1 | 100.0 | [Object] |
| fn.code | String | 1 | 100.0 | function (x, y){ return x + y; } |
| name | String | 1 | 100.0 | John |
| nil | null | 1 | 100.0 | [null] |
| uid | BinData-UUID | 1 | 100.0 | 3b241101e2bb42558caf4136c566a962 |
+--------------------------------------------------------------------------------------------+

Variety captures each `lastValue` from the first matching document it sees in the configured sort order. If you do not provide `sort`, it uses the default `{ _id: -1 }` ordering, so these values come from the newest matching documents encountered during the scan.
`Date` is converted into an ISO 8601 string, `ObjectId` into `string`, and binary data into hex. Other types are shown in square brackets.
Variety reports BSON wrapper types such as `Decimal128`, `Timestamp`, `Code`, `BSONRegExp`, `MinKey`, `MaxKey`, and `DBRef` by their BSON type names in the `types` column. BSON `Double` and `Int32` are the current exception: the shell-backed analysis path receives both as plain JavaScript numbers, so Variety reports both as `Number`.

## Collect Multiple Example Values

When a single representative value is not enough, use `maxExamples` to gather up to N sample values per key. Unlike `lastValue`, which captures only one value from the first matching document, `maxExamples` accumulates examples across all analyzed documents (up to the specified count). Unlike [`limit`](#analyze-only-recent-documents), it does not change which documents are analyzed — all documents still contribute to the schema; `maxExamples` only controls how many sample values are retained per key in the output.

$ mongosh test --eval "var collection = 'users', maxExamples = 3" variety.js

+---------------------------------------------------------------------------------------------------------------------------------------------------+
| key | types | occurrences | percents | examples |
| ------------------ | -------------------- | ----------- | -------- | ---------------------------------------------------------------------------- |
| _id | ObjectId | 5 | 100.0 | 69e6cbc2e9ccbf038c44ba8d, 69e6cbc2e9ccbf038c44ba8c, 69e6cbc2e9ccbf038c44ba8b |
| name | String | 5 | 100.0 | Jim, Geneviève, Harry |
| bio | String | 3 | 60.0 | Ça va?, I swordfight., A nice guy. |
| birthday | Date | 2 | 40.0 | 1984-03-14T00:00:00.000Z, 1974-03-14T00:00:00.000Z |
| pets | String (1),Array (1) | 2 | 40.0 | egret, [Array] |
| someBinData | BinData-old | 1 | 20.0 | d76df8 |
| someWeirdLegacyKey | String | 1 | 20.0 | I like Ike! |
+---------------------------------------------------------------------------------------------------------------------------------------------------+

_(ObjectId hex strings are runtime-generated; exact values will differ.)_

Via the first-party CLI:

$ variety test/users --maxExamples 3

The same serialisation rules as `lastValue` apply: `Date` → ISO 8601 string, `ObjectId` → hex string, binary data → hex. Non-serialisable types such as `Array` or `Object` appear as `[TypeName]` placeholders.

`maxExamples` and `lastValue` are independent; both can be used together.

_Thanks to [@humanitiesclinic](https://github.com/humanitiesclinic) for suggesting this feature ([#154](https://github.com/variety/variety/issues/154))._

## Output Formats and Plugins

Variety ships two built-in output formats. For custom output, plugins can supply their own formatter.

### Built-in Formats

Two formatters are included out of the box:

| Format | Description |
| --- | --- |
| `ascii` (default) | Padded table, as shown throughout this README |
| `json` | Pretty-printed JSON array, suitable for piping to other tools |

Select a format with the `outputFormat` option:

$ mongosh test --quiet --eval "var collection = 'users', outputFormat='json'" variety.js

Passing an unrecognised value throws an error listing the valid options.

### Hide Frequency Columns in ASCII Output

If you only care about schema shape and type coverage, set `hideFrequencyColumns`
to `true` to remove the `occurrences` and `percents` columns from the built-in
ASCII table:

$ mongosh test --eval "var collection = 'users', hideFrequencyColumns = true" variety.js

Via the first-party CLI:

$ variety test/users --hide-frequency-columns

+-------------------------------------------+
| key | types |
| ------------------ | -------------------- |
| _id | ObjectId |
| name | String |
| bio | String |
| birthday | Date |
| pets | String (1),Array (1) |
| someBinData | BinData-old |
| someWeirdLegacyKey | String |
+-------------------------------------------+

This option affects only built-in ASCII output. JSON output and persisted
results still include `totalOccurrences` and `percentContaining`.

### Plugins

A plugin is a `.js` file that exports a plain object with lifecycle hooks. Load one with the `plugins` option (comma-separated for multiple):

$ mongosh test --quiet --eval "var collection='users', plugins='/path/to/my-plugin.js'" variety.js

Via the first-party CLI (use `--plugin` once per plugin):

$ variety test/users --quiet --plugin /path/to/my-plugin.js

Pass per-plugin configuration by appending `|key=value` after the path:

$ mongosh test --quiet --eval "var collection='users', plugins='/path/to/my-plugin.js|delimiter=;'" variety.js

Via the first-party CLI (use `?key=value` instead of `|`, and `&` between multiple params):

$ variety test/users --quiet --plugin '/path/to/my-plugin.js?delimiter=;'

For multiple plugins, repeat the flag:

$ variety test/users --quiet --plugin /path/to/formatter.js --plugin '/path/to/other.js?key=val'

See [CONTRIBUTING.md](CONTRIBUTING.md) for the full hook reference and a guide to writing and testing plugins.

### Quiet Option

Both MongoDB and Variety output some additional information to standard output. If you want to remove this info, you can use the `--quiet` option provided by the MongoDB shell executable.
Variety can also read that option and mute unnecessary output. This is useful in connection with `outputFormat=json`. You would then receive only JSON, without any other characters around it.

$ mongosh test --quiet --eval "var collection = 'users', outputFormat='json', sort = { updated_at : -1 }" variety.js

### Log Keys and Types As They Arrive Option
Sometimes you want to see the keys and types come in as it happens. Maybe you have a large dataset and want accurate results, but you also are impatient and want to see something now. Or maybe you have a large mangled dataset with crazy keys (that probably shouldn't be keys) and Variety is going out of memory. This option will show you the keys and types as they come in and help you identify problems with your dataset without needing the Variety script to finish.

$ mongosh test --eval "var collection = 'users', sort = { updated_at : -1 }, logKeysContinuously = true" variety.js

Via the first-party CLI:

$ variety test/users --sort '{"updated_at":-1}' --log-keys-continuously

### Exclude Subkeys
Sometimes you inherit a database full of junk. Maybe the previous developer put data in the database keys, which causes Variety to go out of memory when run. After you've run the `logKeysContinuously` to figure out which subkeys may be a problem, you can use this option to run Variety without those subkeys.

db.users.insertOne({name:"Walter", someNestedObject:{a:{b:{c:{d:{e:1}}}}}, otherNestedObject:{a:{b:{c:{d:{e:1}}}}}});

$ mongosh test --eval "var collection = 'users', sort = { updated_at : -1 }, excludeSubkeys = [ 'someNestedObject.a.b' ]" variety.js

Via the first-party CLI (repeat `--exclude-subkeys` for multiple paths):

$ variety test/users --sort '{"updated_at":-1}' --exclude-subkeys someNestedObject.a.b

+-----------------------------------------------------------------+
| key | types | occurrences | percents |
| --------------------------- | -------- | ----------- | -------- |
| _id | ObjectId | 1 | 100.0 |
| name | String | 1 | 100.0 |
| someNestedObject | Object | 1 | 100.0 |
| someNestedObject.a | Object | 1 | 100.0 |
| someNestedObject.a.b | Object | 1 | 100.0 |
| otherNestedObject | Object | 1 | 100.0 |
| otherNestedObject.a | Object | 1 | 100.0 |
| otherNestedObject.a.b | Object | 1 | 100.0 |
| otherNestedObject.a.b.c | Object | 1 | 100.0 |
| otherNestedObject.a.b.c.d | Object | 1 | 100.0 |
| otherNestedObject.a.b.c.d.e | Number | 1 | 100.0 |
+-----------------------------------------------------------------+

### Show Array Elements

By default, Variety suppresses keys that end with an array index (e.g. `tags.XX`), because the parent key already captures the `Array` type. If you want to see the types of the values _inside_ primitive arrays — useful for verifying element-type consistency — set `showArrayElements` to `true`:

$ mongosh test --eval "var collection = 'users', showArrayElements = true" variety.js

Via the first-party CLI:

$ variety test/users --show-array-elements

For example, given documents like:

db.users.insertMany([
{ name: 'Alice', tags: ['a', 'b'] },
{ name: 'Bob', tags: ['c', 1] }
]);

Without `showArrayElements`, only the `tags` key (type `Array`) appears. With it enabled, you also see:

+----------------------------------------------------------+
| key | types | occurrences | percents |
| ------- | --------------------- | ----------- | -------- |
| tags | Array | 2 | 100.0 |
| tags.XX | String (2),Number (1) | 2 | 100.0 |
+----------------------------------------------------------+

This reveals that `tags` contains mixed element types across the collection.

### Compact Array Types

If you want the parent array key itself to carry a more informative summary than plain `Array`, set `compactArrayTypes` to `true`:

$ mongosh test --eval "var collection = 'users', compactArrayTypes = true" variety.js

Via the first-party CLI:

$ variety test/users --compact-array-types

With this option enabled, parent keys can be reported as values such as `Array(String)`, `Array(Number|String)`, or `Array(empty)` instead of just `Array`.

This option is complementary to `showArrayElements`: `compactArrayTypes` makes the parent key more descriptive, while `showArrayElements` still exposes the `tags.XX`-style child keys when you want per-element detail.

_Thanks to [@oufeng](https://github.com/oufeng) for suggesting this feature ([#166](https://github.com/variety/variety/issues/166))._

### Secondary Reads
Analyzing a large collection on a busy replica set primary could take a lot longer than if you read from a secondary. To do so, tell MongoDB to use the `secondary` read preference
by setting the `secondaryOk` property to `true`:

$ mongosh secondary.replicaset.member:31337/somedb --eval "var collection = 'users', secondaryOk = true" variety.js

Via the first-party CLI:

$ variety somedb/users --host secondary.replicaset.member --port 31337 --secondary-ok

`secondaryOk` replaces the old `slaveOk` name; see [issue #309](https://github.com/variety/variety/issues/309).

## Save Results in MongoDB For Future Use
By default, Variety prints results only to standard output and does not store them in MongoDB itself. If you want to persist them automatically in MongoDB for later usage, you can set the `persistResults` parameter.
Variety then stores result documents in the `varietyResults` database, and the collection name is derived from the source collection's name.
If the source collection's name is `users`, Variety stores results in the `usersKeys` collection under the `varietyResults` database.

$ mongosh test --quiet --eval "var collection = 'users', persistResults=true" variety.js

Via the first-party CLI:

$ variety test/users --quiet --persist-results

To persist to an alternate MongoDB database, you may specify the following parameters:

- `resultsDatabase` - The database to store Variety results in. Accepts either a database name or a `host[:port]/database` URL.
- `resultsCollection` - Collection to store Variety results in. **WARNING:** This collection is dropped before results are inserted.
- `resultsUser` - MongoDB username for results database
- `resultsPass` - MongoDB password for results database

```
$ mongosh test --quiet --eval "var collection = 'users', persistResults=true, resultsDatabase='db.example.com/variety'" variety.js
```

Via the first-party CLI:

```bash
variety test/users --quiet --persist-results --results-database db.example.com/variety --results-collection myKeys --results-user reporter --results-password secret
```

## Reserved Keys
Variety expects keys to be well formed, not having any `.`s in them (MongoDB 2.4 allows dots in certain cases). Also MongoDB uses the pseudo keys `XX` and keys corresponding to the regex `XX\d+XX.*` for use with arrays. You can change the string `XX` in these patterns to whatever you like if there is a conflict in your database using the `arrayEscape` parameter.

$ mongosh test --quiet --eval "var collection = 'users', arrayEscape = 'YY'" variety.js

Via the first-party CLI:

$ variety test/users --array-escape YY

## Command Line Interface
This NPM package publishes a built-in `variety` executable that resolves the bundled `variety.js`, prefers `mongosh` when available, and falls back to the legacy `mongo` shell.

The primary interface is:

```bash
variety DB/COLLECTION [options]
```

Examples:

```bash
variety test/users
```

```bash
variety test/users --outputFormat json --quiet
```

```bash
variety logs/webserver --limit 100 --maxDepth 3 --sort '{"created":-1}'
```

```bash
variety test/users --query '{"bio":{"$exists":true}}' --host localhost --port 27017
```

### Authenticated Remote Databases

For a remote MongoDB instance that requires authentication, pass the connection
details through the built-in CLI:

```bash
variety app/users --host db.example.com --port 27017 --username report_reader --password secret --authenticationDatabase admin --quiet
```

The built-in CLI also supports MongoDB connection strings directly:

```bash
variety app/users --uri "mongodb://report_reader:secret@db.example.com:27017/app?authSource=admin" --quiet
```

`--uri` accepts both `mongodb://` and `mongodb+srv://` connection strings while
keeping the existing `DB/COLLECTION` target syntax:

- If the URI omits a database name, Variety uses the positional `DB`.
- If the URI includes a database name, it must match the positional `DB`.
- `--uri` cannot be combined with `--host`, `--port`, `--username`,
`--password`, or `--authenticationDatabase`.

The equivalent direct Mongo shell invocation uses the connection string as the
shell target and sets the collection through `--eval`:

```bash
mongosh "mongodb://report_reader:secret@db.example.com:27017/app?authSource=admin" --quiet --eval "var collection = 'users'" variety.js
```

Structured flags such as `--query` and `--sort` accept strict JSON. Connection
flags such as `--host`, `--port`, `--username`, `--password`, `--authenticationDatabase`,
`--uri`, and `--quiet` shape the Mongo shell invocation. `--eval` remains
available as an escape hatch when you need to append raw JavaScript assignments.

If the JavaScript you need to append begins with `--` and also matches a Variety CLI flag name, use the inline form `--eval=...` so the parser does not treat it as another option.

When you invoke `variety` with no CLI arguments, the documented compatibility environment variables remain supported:

| Variable | Description |
| --- | --- |
| `DB` | MongoDB database name to pass to the shell |
| `EVAL_CMDS` | JavaScript assignments forwarded via `--eval` (e.g. `var collection = 'users', limit = 100`) |
| `VARIETYJS_DIR` | Directory containing `variety.js`; when omitted, the CLI uses the bundled script |

Examples:

```bash
DB=test EVAL_CMDS="var collection = 'users', outputFormat='json'" variety
```

```bash
DB=test EVAL_CMDS="var collection = 'users', maxDepth = 3, limit = 500" variety
```

Direct `mongosh ... variety.js` usage remains supported and is still useful when you want the most transparent low-level invocation for debugging or advanced shell work.

### Loading Variety From an Interactive Mongo Shell

If you are already inside `mongosh` or the legacy `mongo` shell, define the
same globals you would normally pass with `--eval`, then load `variety.js`:

```js
var collection = 'users';
load('/path/to/variety.js');
```

The `load()` call executes Variety immediately; there is no separate function
to call after the script loads. Set any additional options before `load()`:

```js
var collection = 'users';
var outputFormat = 'json';
var limit = 100;
load('/path/to/variety.js');
```

This low-level form is useful for exploratory shell sessions or debugging. For
regular terminal use, prefer the packaged `variety DB/COLLECTION [options]`
command.

Hat tip: [@abrin](https://github.com/abrin) ([issue #131](https://github.com/variety/variety/issues/131)). Follow-up: [issue #264](https://github.com/variety/variety/issues/264)

### Calling Variety From Node.js

Variety can be automated from a Node.js application by spawning the packaged
`variety` executable, but it is not an in-process Node.js library API. For
machine-readable output, run the CLI with `--outputFormat json --quiet` and
parse stdout:

```js
const { execFile } = require('node:child_process');
const { promisify } = require('node:util');

const execFileAsync = promisify(execFile);

async function analyzeCollection() {
const { stdout } = await execFileAsync('variety', [
'test/users',
'--outputFormat',
'json',
'--quiet',
]);

return JSON.parse(stdout);
}
```

Be careful exposing this pattern from a web endpoint. Keep database credentials
server-side, validate collection names and options, and pass arguments as an
array instead of constructing a shell command from request input. If an endpoint
only needs the available field names, the MongoDB Node.js driver may be a better
fit than shelling out to Variety for each request.

Hat tip: [@ashishtilara](https://github.com/ashishtilara) ([issue #125](https://github.com/variety/variety/issues/125)). Follow-up: [issue #263](https://github.com/variety/variety/issues/263)

Note: `variety-cli`, a formerly available companion project that offered higher-level argument parsing, has been archived and is no longer maintained.

## "But my dad told me MongoDB is a schemaless database!"

First of all, your father is a great guy. Moving on…

A MongoDB collection does not enforce a predefined schema like a relational database table. Still, documents in real-world collections nearly always have large sections for which the format of the data is the same. In other words, there is a schema to the majority of collections, it's just enforced by the _application_, rather than by the database system. And this schema is allowed to be a bit fuzzy, in the same way that a given table column might not be required in all rows, but to a much greater degree of flexibility. So we examine what percent of documents in the collection contain a key, and we get a feel for, among other things, how crucial that key is to the proper functioning of the application.

## Dependencies

The packaged `variety` executable runs on Node.js and a MongoDB shell (`mongosh` is preferred; the legacy `mongo` shell still works where available). If you run Variety directly as `mongosh ... variety.js`, the shell path still depends only on MongoDB plus a MongoDB shell.

## Contributing

For setup, repo layout and the `variety.js` build, testing, linting, and how to report issues or send patches, see [CONTRIBUTING.md](CONTRIBUTING.md).

## Core Maintainers

- Tomáš Dvořák ([LinkedIn](https://www.linkedin.com/in/dvoraktomas/))
- Eve Freeman ([Github](https://github.com/freeeve))
- James Cropcho (original creator of Variety) ([Twitter/X](https://x.com/Cropcho))

## Special Thanks

Additional special thanks to Gaëtan Voyer-Perraul ([@gatesvp](https://x.com/gatesvp)) and Kristina Chodorow ([@kchodorow](https://x.com/kchodorow)) for answering other people's questions about how to do this on Stack Overflow, thereby providing me with the initial seed of code which grew into this tool.

Much thanks also, to Kyle Banker ([@Hwaet](https://x.com/hwaet)) for writing an unusually good book on MongoDB, which has taught me everything I know about it so far.

## Tools Which Use Variety (Open Source)

Know of one? Built one? Let us know!

## Stay Safe

I have every reason to believe this tool will **not** corrupt your data or harm your computer. But if I were you, I would not use it in a production environment.

Released by James Cropcho, © 2012–2026, under the [MIT License](https://opensource.org/license/MIT).